一、基本概念
(1)公共的资源称为临界资源;
(2)访问临界资源的代码称为临界区;
(3)互斥,为了保护临界区/临界资源的安全性;
(4)同步,访问临界资源时需要按照一定的顺序;
(5)原子性,对目标数据要么改了,要么没改,不存在改一部分的情况。
二、mutex(互斥量)
·大部分情况,线程使用的数据都是局部变量,变量的地址空间在线程栈
空间内,这种情况,变量归属单个线程,其他线程无法获得这种变量;
·有些情况,很多变量都需要在线程间共享,这样的变量称为共享变量,可以通过数据的共享,完成线程之间的交互;
·多个线程并发的操作共享某些变量,会带来一些问题;
·多个线程之间所定义的全局变量属于临界资源。
刚上面提到 多个线程并发的操作共享变量,会带来某些问题,如何解决这个问题:
(1)代码必须要有互斥行为:当代码进入临界区执行时,不允许其他线程进入该临界区;
(2)若多个线程同时要求执行临界区的代码,并且临界区没有线程在执行,那么只能允许一个线程进入该临界区;
(3)如果线程不在临界区中执行,那么该线程不能阻止其他线程进入临界区。
·初始化互斥量
方法1:静态分配
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER
方法2:动态分配
int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);
参数:
mutex:要初始化的互斥量
attr:NULL
·销毁互斥量
销毁互斥量需要注意的有:
·使用PTHREAD_MUTEX_INITIALIZER初始化的互斥量不需要销毁;
·不要销毁一个已经加锁的互斥量;
·已经销毁的互斥量,要确保后面不会有线程再尝试加锁。
int pthread_mutex_destroy(pthread_mutex_t *mutex);
·互斥量加锁和解锁
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
返回值:成功返回0,失败返回错误码。
调用pthread_lock时,可能会遇到以下情况:
·互斥量处于未锁状态,该函数会将互斥量上锁,同时返回成功;
·发起函数调用时,其他线程已经锁定互斥量,或者其他线程同时申请互斥量,但没有竞争到互斥量,那么pthread_lock调用会陷入阻塞,等待互斥量解锁。
三、同步量
·条件变量
条件变量要使用时必须和互斥锁搭配起来。
一个线程可以通过条件变量来通知另一个线程某件事情是否发生;
为了实现同步功能;
·条件变量函数
初始化:
int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);
参数:
cond:要初始化的条件变量
attr:NULL
销毁:
int pthread_cond_destroy(pthread_cond_t *cond)
等待条件满足:
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
参数:
cond:要在这个条件变量上等待
mutex:互斥量
唤醒等待:
唤醒多个条件变量
int pthread_cond_broadcast(pthread_coond_t *cond);
唤醒一个条件变量
int pthread_cond_signal(pthread_cond_t *cond);
简单案例:
生产者—–消费者模型(321);
在该模型中存在着321原则,具体理解为:在该模型中具有3种关系、2类角色、1个交易场所;
生产者与生产者:互斥关系;
消费者与消费者:互斥关系;
生产者与消费者:互斥且同步
四、POSIX信号量
POSIX信号量和SystemV信号量作用相同,都是用于同步操作,达到无冲突的访问共享资源的目的,但POSIX可以用于线程间同步。
·信号量本质上市一个临界资源,必须保证它是原子的;
·信号量本质上也是一个计数器,是用来描述临界资源的计数器;
·线程只要能够申请到信号量,那么临界资源中肯定有一份资源属于线程;
·申请信号量,进行P操作(减1),释放信号量,进行V操作(加1);PV操作必须保证信号量的原子性。
初始化信号量:
#include<semaphore.h>
int seem_init(sem_t *sem,int pshared,unsigned int value);
参数:
pshared:0表示线程间共享,非零表示进程间共享
value:信号量初始值
销毁信号量:
int sem_destroy(sem_t *sem);
等待信号量:
功能:等待信号量,会将信号量的值减1
int sem_wait(sem_t *sem);
发布信号量:
功能:发布信号量,表示资源使用完毕,可以归还资源了,将信号量值加1
int sem_post(sem_t *sem);
五、读写锁
·对于读写锁,写是互斥的,读是共享的,写锁的优先级高于读(即写者优先);
·读写锁的本质上是一把自旋锁(自旋锁不会被挂起);
·互斥量和信号量本质上是一种挂起等待锁。
读写锁接口
初始化:
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,const pthread_rwlockattr_t *restrict attr);
销毁:
int pthread_rwlock_ddestroy(pthread_rwlock_t *rwlock);
加锁和解锁:
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); //读加锁
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); //写加锁
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
读者—-写者模型:
读者---读者:共享关系
写者---写者:互斥关系
读者---写者:同步且互斥
读者---写者模型与生产者---消费者模型区别的本质是:
生产者---消费者模型中的消费者会将数据拿走,而读者---写者模型中没有。