目录
一、相关概念
1.互斥的定义
2.同步的定义
二、互斥问题及解决方案
1.互斥锁(mutex)(独占锁)
(1)互斥锁机制
(2)互斥锁的操作
a.申请互斥锁---->
b.上锁---->pthread_mutex_lock
c.解锁---->pthread_mutex_unlock
d.回收互斥锁---->pthread_mutex_destroy
(3)互斥锁具体使用
2.读写锁(rwlock)
(1)读写锁机制
(2)读写锁的操作
a.申请初始化读写锁---->
b.读上锁---->pthread_rwlock_rdlock
c.写上锁---->pthread_rwlock_wrlock
d.解锁---->pthread_rwlock_unlock
e.回收互斥锁---->pthread_rwlock_destroy
(3)读写锁具体使用
3.信号量(semaphore)
(1)信号量机制
(2)信号量应用场景
(3)信号量典型应用
三、同步问题及解决方案
1.条件变量
(1)条件变量机制
(2)条件变量的操作
a.申请初始化条件变量---->
b.条件变量阻塞---->pthread_cond_wait
c.条件变量通知---->pthread_cond_signal
---->pthread_cond_broadcast
d.回收条件变量---->pthread_cond_destroy()
(3)条件变量的应用场景
2.信号量(semphore)(POSIX标准)
(1)POSIX有名信号量:(用于进程间同步互斥)
a.创建---->sem_open
(2)POSIX无名信号量:(用于线程间同步互斥)
a.创建---->sem_init
b.P操作---->sem_wait
c.V操作---->sem_post
d.回收---->sem_destroy
(3)信号量用于同步问题解决
(4)信号量用于同步问题的典型应用
对同一资源的访问同时仅允许一个访问者,互斥具有唯一性与排他性,互斥无法保证访问者的访问顺序
在互斥的基础上,实现对同一资源的有序访问
a.互斥锁是内核提供的一个用于共享资源互斥访问的内核资源。
b.互斥锁有两种状态:上锁、解锁
c.如果一个线程希望上锁一个已经被上锁的互斥锁,则线程阻塞,直到互斥锁处于解锁状态。
d.如果每个线程在共享资源访问前都对互斥锁进行上锁操作,那么系统只能满足一个线程的上锁操作请求,其他线程将被阻塞,这样就可以保证共享资源在同一时刻仅有一个访问者进行访问。
a.申请互斥锁---->
---->静态初始化
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
---->动态初始化
pthread_mutex_init
//头文件
#include
//函数原型
int pthread_mutex_init(pthread_mutex_t mutex,pthread_mutexattr_t attr);
①函数功能: 初始化互斥锁
②函数参数: mutex:待初始化的互斥锁
attr:锁属性,NULL代表快速互斥锁,attr 取值可以为以下3种:
PTHREAD_MUTEX_INITIALIZER: 快速互斥锁
PTHREAD_RECURSIVE_MUTEX_INITIALIZER_UP: 递归互斥锁
PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_UP: 检错互斥锁
③函数返回值: 成功 : 返回0
失败 : 返回 错误码
b.上锁---->
pthread_mutex_lock
//头文件
#include
//函数原型
int pthread_mutex_lock(pthread_mutex_t *mutex);
①函数功能: 上锁互斥锁
②函数参数: mutex: 待操作的互斥锁
③函数返回值: 成功 : 返回0
失败 : 返回 错误码
c.解锁---->
pthread_mutex_unlock
//头文件
#include
//函数原型
int pthread_mutex_unlock(pthread_mutex_t *mutex);
①函数功能: 解锁互斥锁
②函数参数: mutex: 待操作的互斥锁
③函数返回值: 成功 : 返回0
失败 : 返回 错误码。
d.回收互斥锁---->
pthread_mutex_destroy
//头文件
#include
//函数原型
int pthread_mutex_destroy(pthread_mutex_t *mutex);
①函数功能: 回收互斥锁
②函数参数: mutex: 待操作的互斥锁
③函数返回值: 成功 : 返回0
失败 : 返回 错误码。
a.共享资源访问前上锁;
b.共享资源访问结束后解锁;
a.与互斥锁类似,只是读写锁在上锁操作时,细化为读上锁和写上锁两种操作。
b.读上锁可以让线程共享访问同一资源,写上锁只能独占访问资源。
c.如果针对读访问与写访问的场合,读访问者在同一资源访问前读上锁,不阻塞可以共享访问,无疑会提高资源的访问效率
d.使用场合
读访问者数量 > 1
a.申请初始化读写锁---->
---->静态初始化
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
---->动态初始化
pthread_rwlock_init
//头文件
#include
//函数原型
int pthread_rwlock_init(pthread_rwlock_t rwlock,pthread_rwlockattr_t attr);
①函数功能: 初始化读写锁
②函数参数: rwlock:待初始化的读写锁
attr: 锁属性,NULL代表快速读写锁
③函数返回值: 成功 : 返回0
失败 : 返回 错误码
b.读上锁---->
pthread_rwlock_rdlock
//头文件
#include
//函数原型
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
①函数功能: 读上锁读写锁
②函数参数: mutex: 待操作的读写锁
③函数返回值: 成功 : 返回0
失败 : 返回 错误码。
c.写上锁---->
pthread_rwlock_wrlock
//头文件
#include
//函数原型
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
①函数功能: 读上锁读写锁
②函数参数: mutex: 待操作的读写锁
③函数返回值: 成功 : 返回0
失败 : 返回 错误码。
d.解锁---->
pthread_rwlock_unlock
//头文件
#include
//函数原型
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
①函数功能: 解锁读写锁
②函数参数: mutex: 待操作的读写锁
③函数返回值: 成功 : 返回0
失败 : 返回 错误码。
e.回收互斥锁---->
pthread_rwlock_destroy
//头文件
#include
//函数原型
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
①函数功能: 回收读写锁
②函数参数: rwlock:待操作的读写锁
③函数返回值: 成功 : 返回0
失败 : 返回 错误码。
a.共享资源访问前,如果访问者是读访问资源,读上锁;
b.共享资源访问前,如果访问者是写访问资源,写上锁;
c.共享资源访问结束后解锁;
信号量用于线程互斥,往往仅需要一个信号量,且将信号量的初值设为资源总数;
每个线程在访问共享资源前对该信号量做P操作,资源访问结束后,对信号量做V操作。
资源总数 > 1
哲学家就餐
a.条件变量不像互斥锁那样竞争访问共享资源,条件变量是用来阻塞线程的,直到某个条件成立,条件变量要搭配互斥锁一起使用,原因是条件的判断是要在互斥锁的保护下进行的
b.也就是说,在条件判断的同时,条件是不能被其他线程修改的。
c.条件变量在阻塞第一个线程的同时,会解开用于保护条件的互斥锁。
d.更改条件的线程,可以向条件变量发送信号通知,唤醒被阻塞的线程,线程重新评估条件,再决定是否继续运行
a.申请初始化条件变量---->
静态初始化---->
pthread_cond_t cond = PTHREAD_COND_INITIALIZER
动态初始化---->
pthread_cond_init()
//头文件
#include
//函数原型
int pthread_cond_init(pthread_cond_t* cond,pthread_condattr_t* attr);
①函数功能:初始化条件变量
②函数参数
cond:待初始化的条件变量
attr: 条件变量属性,NULL代表缺省属性:
③函数返回值
成功 : 返回0
失败 : 返回 错误码
b.条件变量阻塞---->
pthread_cond_wait
//头文件
#include
//函数原型
int pthread_cond_wait(pthread_cond_t cond,pthread_mutex_t mutex);
①函数功能: 用条件变量阻塞调用线程,同时解锁mutex
②函数参数: cond:待初始化的条件变量
mutex:待解锁的互斥锁:
③函数返回值: 成功 : 返回0
失败 : 返回 错误码。
c.条件变量通知---->
pthread_cond_signal
---->
pthread_cond_broadcast
//头文件
#include
//函数原型
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);
①函数功能: 通知条件变量,唤醒被条件变量阻塞的线程
②函数参数: cond: 待初始化的条件变量
③函数返回值: 成功 : 返回0
失败 : 返回 错误码。
d.回收条件变量---->
pthread_cond_destroy()
//头文件
#include
//函数原型
int pthread_cond_destroy(pthread_cond_t *cond);
①函数功能: 回收条件变量
②函数参数: cond: 待初始化的条件变量
③函数返回值: 成功 : 返回0
失败 : 返回 错误码。
对共享资源的有条件访问
a.创建---->
sem_open
//头文件
#include
#include
#include
//函数原型
sem_t* sem_open(const char *name, int oflag);
sem_t *sem_open(const char *name, int oflag,
mode_t mode, unsigned int value);
①函数功能: 创建或者打开Posix有名信号量
②函数参数: name:有名信号量名称
oflag:有名信号量标志:
mode: 若创建,mode代表新创建的信号量权限
value:新创建信号量的初值
③函数返回值: 成功:返回新创建信号量地址
失败:返回 SEM_FAILED。错误码放在errno中
a.创建---->
sem_init
//头文件
#include
//函数原型
int sem_init(sem_t *sem,int pshared,int value);
①函数功能: 初始化posix无名信号量
②函数参数: sem:待初始化的信号量
psharead: 只能指定为0
value: 信号量的初值
③函数返回值:成功:返回0
失败:-1 错误码放在errno。
b.P操作---->
sem_wait
//头文件
#include
//函数原型
int sem_wait(sem_t *sem,pthread_mutex_t *mutex);
①函数功能: 阻塞调用线程,并释放mutex互斥锁
②函数参数: sem:用于阻塞线程的信号量
mutex: 待解锁的互斥锁:
③函数返回值:成功:返回0
失败: -1 错误码放在errno。
c.V操作---->
sem_post
//头文件
#include
//函数原型
int sem_post(sem_t *sem);
①函数功能: 对信号量值+1
②函数参数: sem:待操作的信号量
③函数返回值: 成功:返回0
失败:-1 错误码放在errno。
d.回收---->
sem_destroy
//头文件
#include
//函数原型
int sem_destroy(sem_t *sem);
①函数功能: 回收信号量
②函数参数: sem:待操作的信号量
③函数返回值: 成功:返回0
失败 : -1 错误码放在errno。
a.信号量用于同步问题,往往需要多个信号量,让其中一个信号量的初值设为资源总数,其余都设为0;
b.若想让某个线程先访问共享资源,在访问共享资源前让其对初值非0的信号量做P操作,其余线程在共享资源访问前对初值为0的信号量做P操作
c.共享资源访问结束后,先访问资源的线程对初值为0的信号量做V操作,其他线程访问结束后,对初值非0的信号量做V操作。
a.生产者消费者问题:也叫有限缓冲问题
b.该问题描述了两个线程(生产者/消费者)对共享缓冲区访问,生产者向缓冲区写入数据,消费者从缓冲区读取数据
c.该问题的关键
①保证生产者线程在共享缓冲区满容量时不允许访问
②保证消费者在共享缓冲区为空时,不能读取数据