多线程的虚假唤醒理解

大耗子 2020年11月17日 46次浏览

虚假唤醒

  • 在多线程环境中,在竞争资源的时候,有时候拿到了锁,却发现资源没了,这可能就是使用了条件等待产生的一个惊群效应。
  • pthread_cond_signal将所有的pthread_cond_wait线程都唤醒了,但是只有个别线程竞争到了资源,没有竞争资源的线程就是属于虚假唤醒的线程。
  • 对于虚假唤醒的线程需要做特殊判断,在获取到mutex锁后,要去判断资源是不是为空,是空就是虚假唤醒,继续睡眠。

是否有可能多个线程同时读到一个数据的情况呢?

这就要从pthread_cond_wait函数的实现讲起。

  1. 在pthread_cond_wait函数中,在调用pthread_cond_wait之前得先获取到mutex互斥锁。
  2. 在调用pthread_cond_wait的时候,函数程序会先检查是否符合条件,不符合条件,就将互斥锁mutex释放掉。
  3. 将线程加入等待队列,然后进入睡眠。
  4. 当其他线程调用pthread_cond_signal后,所有在等待队列上的线程竞争互斥锁mutex。
  5. 拿到mutex后,就去消费资源。如果没有拿到互斥锁的,继续竞争。
  6. 如果此时资源消耗完毕,线程因为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);
        }


}