以下内容引述至《Linux/Unix系统编程手册》
线程同步方式
互斥量 和 条件变量
互斥量
一般情况下,对每一共享资源(可能由多个先关变量组成)会使用不同的互斥量,每一个线程在访问同一资源时将采用如下协议
- 针对共享资源锁定互斥量
- 访问共享资源
- 对互斥量解锁
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex *mutex);
int pthread_mutex_unlock(pthread_mutex *mutex);
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
互斥量类型
- 同一线程不应对同一互斥量加锁两次
- 线程不应对不为自己所拥有的互斥量解锁
- 线程不应对一尚未锁定的互斥量做解锁动作
PTHREAD_MUTEX_NORMAL
该类型的互斥量不具有死锁检测功能,如果线程试图对已由自己锁定的互斥量枷锁,则发生死锁。
PTHREAD_MUTEX_ERRORCHECK
对此类互斥量的所有操作都会执行错误检查。这类互斥量运行起来比一般类型要慢
PTHREAD_MUTEX_RECURSIVE
递归互斥量维护有一个锁计数器。当线程第一次去的互斥时,会将锁计数器置1,后续由同一线程执行的每次加锁操作会递增锁计数器的数值,而解锁操作则递减计数器计数。只有当锁计数器降至0时,才会释放该互斥量。
条件变量
互斥量防止多个线程同时访问同一共享变量,条件变量允许一个线程就某个共享变量(或其他共享资源)的状态变化通知其他线程,并让其他线程等待这一通知。
#include <pthread.h>
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
int pthread_cond_timewait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime);
int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);
int pthread_cond_destroy(pthread_cond_t *cond);
条件变量与互斥量之间存在天然的关联关系
- 线程在准备检查共享变量状态时锁定互斥量;
- 检查共享变量的状态;
- 如果共享变量未处于预期状态,线程应在等待条件变量并进入休眠前解锁互斥量(以便其他线程能访问该共享变量);
- 当线程因为条件变量的通知而被再度唤醒时,必须对互斥量再次加锁,因为在典型情况下,线程会立即访问共享变量
static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
static ptherad_cond_t cond = PTHREAD_COND_INITIALIZER;
static int avail = 0;
// 生产者
s = pthread_mutex_lock(&mtx);
if(s!=0)
// do log
avail ++;
s = pthread_mutex_unlock(&mtx);
if(s!=0)
// do log
s = pthread_cond_signal(&cond);l
if(s!=0)
// do log
// 消费者
for(;;){
s = pthread_mutex_lock(&mtx);
if(s!=0)
// do log
while(avail == 0) {
s = pthread_cond_wait(&cond, &mtx);
if(s !=0)
// do log
}
while(avail > 0){
avail--;
}
s = pthread_mutex_unlock(&mtx);
if(s!=0)
// do log
}
通用的设计原则:
必须由一个while循环,而不是if语句,来控制对pthread_cond_wait的调用。因为当代码从pthread_cond_wait()返回时,并不能确定判断条件的状态,所以应该立即重新检查判断条件,在条件不满足的情况下继续休眠等待;
从pthread_cond_wait返回时,之所以不能对判断条件的状态做任何假设,理由如下:
- 其他线程可能会率先醒来,也许有多个线程在等待获取与条件变量相关的互斥量。
- 设计时设置"宽松的"判断条件或许更为简单。有时,用条件变量来表征可能性而非确定性,在设计应用程序时会更加简单。
- 可能会发生虚假唤醒的情况。在一些实现中,即使没有任何其他线程真地就条件变量发出信号,等待此条件变量的线程仍有可能醒来。