虚假唤醒
- 在多线程环境中,在竞争资源的时候,有时候拿到了锁,却发现资源没了,这可能就是使用了条件等待产生的一个惊群效应。
- pthread_cond_signal将所有的pthread_cond_wait线程都唤醒了,但是只有个别线程竞争到了资源,没有竞争资源的线程就是属于虚假唤醒的线程。
- 对于虚假唤醒的线程需要做特殊判断,在获取到mutex锁后,要去判断资源是不是为空,是空就是虚假唤醒,继续睡眠。
是否有可能多个线程同时读到一个数据的情况呢?
这就要从pthread_cond_wait函数的实现讲起。
- 在pthread_cond_wait函数中,在调用pthread_cond_wait之前得先获取到mutex互斥锁。
- 在调用pthread_cond_wait的时候,函数程序会先检查是否符合条件,不符合条件,就将互斥锁mutex释放掉。
- 将线程加入等待队列,然后进入睡眠。
- 当其他线程调用pthread_cond_signal后,所有在等待队列上的线程竞争互斥锁mutex。
- 拿到mutex后,就去消费资源。如果没有拿到互斥锁的,继续竞争。
- 如果此时资源消耗完毕,线程因为pthread_cond_signal的惊群效应醒来而获取到mutex锁,出来后会发现其实资源已经是空了,此时又该继续调用pthread_cond_wait,去到等待队列等待唤醒信号。
所以不会出现读到同一个数据的情况出现。
伪代码
具体实现代码,请看链接
http://codemouse.online/archives/2020-06-08154227
struct JOB{
void (*fun)();
};
struct WORK{
JOB *jobs;
pthread_mutex_t mutex;
pthread_cond_t cond;
};
void thread_fun(char * argv)
{
WORK *work = (WORK*)argv;
JOB *job = NULL;
while(1){
pthread_mutex_lock(&work->mutex);
while(work->jobs == NULL){
// 此处的while用来处理虚假唤醒,如果用if,可能存在没有资源可用的情况。
pthread_cond_wait(&work->mutex,&work->cond);
}
job = work->jobs;
REMOVE(work->jobs);// 移除节点
pthread_mutex_unlock(&work->mutex);
job->fun();
free(job);
}
}