在线程池中线程基于条件变量阻塞的时候有这样一个操作
//无任务状态和不销毁时,线程阻塞等待
while(pool->cur_queue_size == 0 && pool->shutdown != 1)
{
/* printf("线程[0x%lx]正在等待\n",pthread_self()); */
pthread_cond_wait(&(pool->queue_cond),&(pool->queue_lock));//基于条件变量阻塞
}
思考这里为什么要有一个 while(pool->cur_queue_size == 0 && pool->shutdown != 1)这样的操作,没有这个不可以吗?
实际上这里是在阻塞线程前判断了一下任务队列中有没有任务,如果有的话为什么还要阻塞线程然后在唤醒呢。直接不进入while循环而直接去执行任务。
那这里为什么不用if()呢?
这就引出一个虚假唤醒的问题,虚假唤醒就是说唤醒之后任务已经被执行了,该线程白白被唤醒而没有任务,甚至重复执行任务,引起错误操作。
想象这样一个场景:
前情摘要
有一个队列,线程A的任务是先清空队列,然后检查队列是否为空,紧接着清空这个队列,B线程是获取队首元素,C线程讲一个元素入队。
首先,A线程将队列清空。
然后,B线程试图获得队首元素,但是队列目前是空,所以B解锁,基于条件变量阻塞,等待被唤醒。
然后,C线程将一个元素入队,条件变量成立,B线程被唤醒,准备加锁(场景定格),准备获取队首元素(未发生)。
但是,此时可能A线程可能同时要请求加锁(然后检测队列),所以此时A,B线程竞争这个锁。
假如,A得到了这个锁,并且清空了队列,然后释放了锁,此时B线程得到了锁(接上述被唤醒后请求加锁的状态),然后试图获得队首元素,但是此时队列已经为空,所以这是一个虚假唤醒。
解决办法就是用while(1)被唤醒后在检查一下这个任务是否还存在(pool->cur_queue_size == 0)。