线程互斥相关概念
临界资源:多线程执行流共享的资源就叫做临界资源。
临界区:每个线程内部,访问临界资源的代码,就叫做临界区。
互斥:任何时刻,互斥保证有且只有一个执行流进入临界区,访问临界资源,通常对临界资源起保护作用。
原子性:不会被任何调度机制打断的操作,该操作只有两态,要么完成,要么未完成。
大部分情况下,线程使⽤的数据都是局部变量,变量的地址空间在线程栈空间内,这种情况,变量归属单个线程,其他线程⽆法获得这种变量。但有时候,很多变量都需要在线程间共享,这样的变量称为共享变量,也就是临界资源,可以通过数据的共享,完成线程之间的交互。多个线程并发的操作共享的变量,就会出现问题。
要解决上面的问题,需要做到上面三点:
代码必须要有互斥行为:当代码进入临界区执行时,不允许其他线程进入该临界区。
如果多个线程同时要求执行临界区的代码,并且临界区没有线程在执行,那么只能允许一个线程进入该临界区。
如果线程不在临界区中执行,那么该线程不能阻止其他线程进入临界区。
要做到这三点,本质上就是需要一把锁。Linux上提供的这把锁叫互斥量。
互斥量
相当于一把锁,我们在访问临界区之前需要加锁,访问完临界资源之后,再把锁解开。
初始化互斥量
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_mutex_lock时:
互斥量处于未锁状态,该函数会将互斥量锁定,同时返回成功。
发起函数调用时,其他线程已经锁定互斥量,或者存在其他线程同时申请互斥量,但没有竞争到互斥量,那么pthread_ lock调用会陷入阻塞(执行流被挂起),等待互斥量解锁。
条件变量
当一个线程互斥地访问某个变量时,它可能发现在其它线程改变状态之前,它什么也做不了。
如一个线程访问队列时,发现队列为空,它只能等待,只到其它线程将⼀个节点添加到队列中。 这种情况就需要用到条件变量。
初始化条件变量
int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *rest
rict 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 mute
x);
参数:
cond:要在这个条件变量上等待
mutex:互斥量,后⾯详细解释
唤醒等待
int pthread_cond_broadcast(pthread_cond_t *cond);
唤醒所有等待在条件变量上的线程
int pthread_cond_signal(pthread_cond_t *cond);
唤醒第一个等待在条件变量上的线程
条件变量需要使用互斥量
1. 条件等待是线程同步的一种手段,如果只有一个线程,条件不满足,一直等下去都不会满足,所以必须要有一个线程通过某些操作,改变共享变量,使原先不满足的条件变得满足,并且友好的通知等待在条件变量上的线程。
2. 条件不会无缘无故的满足了,必然会牵扯到共享数据的变化,所以一定要用互斥量来保护。没有互斥锁就无法安全的获取和修改共享数据。
假如我们使用pthread_cond_wait即等待条件满足时不加锁,因为等待不是原子操作,那么有可能条件满足,信号发出,但是该信号被错过了,导致线程一直等待,造成死等。所以我们需要对条件变量进行加锁操作。
等待条件代码:
pthread_mutex_lock(&mutex);
while (条件为假)
pthread_cond_wait(cond, mutex);
修改条件
pthread_mutex_unlock(&mutex);
唤醒条件代码:
pthread_mutex_lock(&mutex);
设置条件为真
pthread_cond_signal(cond);
pthread_mutex_unlock(&mutex);