Linux教程

Linux多线程及线程同步

本文主要是介绍Linux多线程及线程同步,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

文章目录

  • 一、线程的概念及实现
    • 1.1、线程的概念:
    • 1.2、线程的三种分类:
    • 1.3、线程和进程的区别:
  • 二、Linux系统,线程实现的方法
    • 2.1线程库的接口介绍
  • 三、线程同步
    • 3.1互斥锁
    • 3.2 (POSIX)信号量
    • 3.3 条件变量
    • 3.4 读写锁
  • 四、线程安全


一、线程的概念及实现

1.1、线程的概念:

线程是进程内的一条执行路径或执行序列; 一个进程可以包含多条线程
在这里插入图片描述

1.2、线程的三种分类:

用户级:
创建开销小,由线程库直接管理, 无法使用多处理器资源

内核级:
创建开销大,由内核直接管理, 可以使用多处理器资源

组合模型:
既可以使用多处理器资源,又能创建多个
在这里插入图片描述

1.3、线程和进程的区别:

  1. 进程是资源分配的最小单位; 线程是CPU调度的最小单位
  2. 进程有自己独立的地址空间; 线程共享进程的地址空间
  3. 进程的创建消耗资源大; 线程的创建消耗相对较小
  4. 进程的切换开销大; 线程的切换开销较小

二、Linux系统,线程实现的方法

2.1线程库的接口介绍

头文件:#include <pthread.h>

1.创建线程

#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, 
	void*(*start_routine)(void*), void *arg);

pthread_create(); 用于创建线程
thread:用于接收创建的线程的ID
attr:指定线程的属性;多为NULL
start_routine: 指定线程函数
arg: 给线程函数传递的参数; 成功返回0,错误返回错误码

2.退出线程

int pthread_exit(void* retval);  //退出线程

pthread_exit(); 退出线程
retval: 指定退出信息

3.等待线程退出

int pthread_join(pthread_t thread, void ** retal);

pthread_join() 等待thread指定的线程退出,线程未退出时该方法阻塞
retal : 接收thread线程退出时,指定的退出信息

三、线程同步

线程同步是指当一个线程在对临界资源进行操作时,其他线程都不可以对该资源进行操作,直到该线程完成操作,其他线程才能操作,也就是协同步调,让线程按照预定先后次序执行。

线程同步的四种方法:互斥锁、信号量、条件变量、读写锁

3.1互斥锁

1、概念: 用于保护关键代码段,确保在任一时刻只能有一个线程访问该资源,既保证在该时刻内其独占式的访问

2.互斥锁的基本API
以下这些函数成功时返回0,失败则返回错误码

  1. 初始化互斥锁
    mutex: 指向要操作的目标互斥锁; 互斥锁的类型是 pthread_mutex_t 结构体
    mutexattr: 指定互斥锁的属性。如果设置为NULL, 则表示使用默认属性
#include<pthread.h>
int pthread_mutex_init(pthread_mutex_t* mutex, const pthread_mutexattr_t* mutexattr);

PS: 也可以这样初始化互斥锁(静态化初始锁):

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

宏PTHREAD_MUTEX_INITIALIZER 实际上只是把互斥锁的各个字段都初始化为0

  1. 加锁
    mutex: 同上, 指向要操作的目标互斥锁
    pthread_mutex_lock 函数以原子操作的方式给一个互斥锁加锁。
    若目标互斥锁已经被加锁, 则该方法的调用将阻塞, 直到该互斥锁的占有者将其解锁
int pthread_mutex_lock(pthread_mutex_t* mutex);
  1. 解锁
    mutex: 同上,指向要操作的目标的互斥锁
    pthread_mutex_unlock 函数以原子操作的方式给一个互斥锁解锁。若此时有其他线程正在等待这个互斥锁, 则这些线程中的某一个将获得它
int pthread_mutex_unlock(pthread_mutex_t* mutex);
  1. 销毁互斥锁
    mutex: 同上,指向要操作的目标互斥锁
    pthread_mutex_destory 函数用于销毁互斥锁。是释放其占用的内核资源。销毁一个已经加锁的互斥锁将导致不可预期的后果
int pthread_mutex_destory(pthread_mutex_t* mutex);

3.2 (POSIX)信号量

以下这些函数成功时返回0,失败则返回-1并设置errno

  1. sem_init 函数
    初始化一个未命名的信号量;
    sem : 指向被操作的信号量
    pshared 指定信号量的类型。若为0,表示这个信号量是当前进程的局部信号量, 否则该信号量就可以在多个进程之间共享。
    value: 指定信号量的初始值(1 或 0)
    若初始化一个已经被初始化的信号量将导致不可预期的结果
#include<semaphore.h>
int sem_init(sem_t* sem, int pshared, unsigned int value);
  1. sem_wait 函数
    以原子操作的方式将信号量的值 减1
    若信号量的值为0,则该函数阻塞,直到这个信号量具有非0值
    sem : 同上,指向被操作的信号量
int sem_wait(sem_t* sem);
  1. sem_post 函数
    以原子操作的方式将信号量的值 加1
    当该信号量的值大于0时, 其他正在调用sem_wait 等待信号量的线程将被唤醒
    sem : 同上,指向被操作的信号量
int sem_post(sem_t* sem);
  1. sem_destory 函数
    用于销毁信号量,释放其占用的内核资源
    若销毁一个正在被其他线程等待的信号量,将导致不可预期的结果
    sem : 同上,指向被操作的信号量
int sem_destory(sem_t* sem);

3.3 条件变量

条件变量提供了一种线程间的通知机制;当某个共享数据达到某个值的时候,唤醒等待这个共享数据的线程
(成功返回0; 失败返回错误码)

  1. pthread_cond_init 函数
    用于初始化条件变量
    cond: 指向要操作的目标条件变量; 条件变量的类型:pthread_cond_t 结构体
    cond_attr: 指定条件变量的属性; 若为NULL, 则使用默认属性
int pthread_cond_init(pthread_cond_t* cond, pthread_conattr_t* cond_attr);
  1. pthread_cond_signal 函数
    用于唤醒一个等待目标条件变量的线程
    cond: 同上,指向要操作的目标条件变量
int pthread_cond_signal(pthread_cond_t* cond);
  1. pthread_cond_broadcast 函数
    以广播的方式唤醒所有等待目标条件变量的线程
    cond: 同上,指向要操作的条件变量
int pthread_cond_broadcast(pthread_cond_t* cond);
  1. pthread_cond_wait 函数
    用于等待目标条件变量
    cond: 同上,指向要操作的条件变量
    mutex: 用于保护条件变量的互斥锁,确保pthread_cond_wait 操作的原子性
    在调用该函数前,必须保证互斥锁 mutex 已经加锁;
    该函数执行时,首先把调用线程放入条件变量的等待队列中,然后将互斥 mutex 解锁。当 pthread_cond_wait 函数成功返回时,互斥锁 mutex 将再次被锁上
int pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* mutex);
  1. pthread_cond_destory 函数
    用于销毁条件变量, 释放其占用的内核资源;
    销毁一个正在被等待的条件变量将失败并返回 EBUSY

3.4 读写锁

细化——锁; 分“读锁”、“写锁”

       未加锁   读加锁   写加锁
 读锁   Yes      Yes      No
 写锁   Yes      No       No

成功返回0; 失败返回错误码

  1. 初始化读写锁
int pthread_rwlock_init(pthread_rwlock_t*rwlock, pthread_rwlockattr_t* attr);
  1. 读——加锁
int pthread_rwlock_rdlock(pthread_rwlock_t* rwlock);
  1. 写——加锁
int pthread_rwlock_wrlock(pthread_rwlock_t* rwlock);
  1. 解锁
int pthread_rwlock_unlock(pthread_rwlock_t* rwlock);
  1. 销毁锁
int pthread_rwlock_t_destory(pthread_rwlock_t* rwlock);

四、线程安全

多线程中无论调度顺序如何,最终的结果都是一样的、正确的。那么说这些线程是安全的

要点:

  1. 对线程同步,保证同一时刻只有一个线程访问临界资源
  2. 在多线程中使用线程安全的函数;安全的函数指:如果一个函数能被多个线程同时调用且不发生静态条件,则它是线程安全的
  3. 线程安全问题大多是由全局变量及静态变量引起的,局部变量逃逸也可能导致线程安全问题。
这篇关于Linux多线程及线程同步的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!