为什么pthread_cond_wait有虚假的唤醒?

引用手册页:

当使用条件variables时,总是有一个布尔谓词涉及与每个条件相关联的共享variables,如果线程继续进行,则wait为true。 可能会发生来自pthread_cond_timedwait()或pthread_cond_wait()函数的虚假唤醒。 由于从pthread_cond_timedwait()或pthread_cond_wait()返回并不意味着这个谓词的值,所以在返回时谓词应该被重新评估。

所以,即使你没有发信号, pthread_cond_wait也可以返回。 乍一看,这似乎很残酷。 这就像一个函数,它在实际返回正确的返回语句之前随机返回错误的值或者随机返回。 这似乎是一个重大的错误。 但是他们select在man page中logging这个事实而不是修正它,这似乎表明, pthread_cond_wait最终虚假地醒来是有正当理由的。 据推测,它是如何工作的内在的东西,使得它不能得到帮助。 问题是什么。

为什么 pthread_cond_wait虚假地返回? 为什么不能保证只有当信号正确时才醒来? 任何人都可以解释其虚假行为的原因吗?

David R. Butenhof在“用POSIX线程编程” (第80页)中给出了以下解释:

虚假唤醒可能听起来很奇怪,但在某些多处理器系统中,使条件唤醒完全可预测可能会大大减慢所有条件variables操作。

在下面的comp.programming.threads讨论中 ,他扩展了devise背后的思想:

 Patrick Doyle写道: 
 >在文章中,汤姆·佩恩写道: 
 Kaz Kylheku写道: 
 >>:这是因为实现有时不能避免插入 
 >>:这些虚假的唤醒; 防止它们可能是昂贵的。 

为什么? 为什么这么难? 例如,我们在说什么 
 >等待信号到达时间超时的情况? 

 >你知道,我不知道pthreads的devise者是否使用这样的逻辑: 
 >条件variables的用户必须在退出时检查条件, 
 >如果我们允许的话,我们不会增加任何额外的负担 
虚假唤醒; 并且因为可以想象允许虚假的 
 >唤醒可以使实现更快,它只能帮助我们 
 >允许他们。 

 >他们可能没有任何具体的实施。 

你实际上并不遥远,除非你没有把它推得太远。 

目的是通过要求谓词循环强制正确/健壮的代码。 这是 
在中国“核心课程”之间由可certificate正确的学术队伍推动 
工作组,但我不认为有人真的不同意这个意图 
一旦他们明白了这是什么意思。 

我们遵循这个意图,有几个层面的理由。 首先是那个 
 “宗教地”使用循环可以保护应用程序免受其自身的不完善 
编码实践。 第二个是抽象的想象并不困难 
机器和实现代码,可以利用这个要求来改善 
平均条件等待操作的性能通过优化 
同步机制。 
 / ------------------ [David.Buten ... @ compaq.com] ------------------ \ 
 | 康柏计算机公司POSIX线程架构师 
 | 我的书:http://www.awl.com/cseng/titles/0-201-63392-2/ | 
 \ ----- [http://home.earthlink.net/~anneart/family/dave.html] ----- / 

至less有两件事“虚假唤醒”可能意味着:

  • 在pthread_cond_wait中被阻塞的线程可以从呼叫中返回,即使没有发生呼叫信号或广播的情况发生。
  • 在pthread_cond_wait中阻塞的线程由于调用信号或广播而返回,但是在重新获取互斥体之后,发现底层谓词不再是真实的。

但是即使条件variables实现不允许前一种情况,后一种情况也会发生。 考虑一个生产者消费者队列和三个线程。

  • 线程1已经出队了一个元素并释放了互斥体,队列现在是空的。 线程正在执行它在某些CPU上获取的元素。
  • 线程2试图出队一个元素,但是当在互斥体中检查时,发现队列是空的,调用pthread_cond_wait,并且在呼叫等待信号/广播中阻塞。
  • 线程3获取互斥锁,向队列中插入一个新元素,通知条件variables,并释放锁。
  • 为了响应来自线程3的通知,正在等待条件的线程2被计划运行。
  • 然而,在线程2pipe理CPU并获取队列锁之前,线程1完成当前的任务,并返回队列进行更多的工作。 它获得队列锁,检查谓词,并发现队列中有工作。 它继续出线插入线程3的项目,释放locking,并执行线程3入队的项目。
  • 线程2现在得到一个CPU并获得锁,但是当它检查谓词时,它发现队列是空的。 线程1'偷了'项目,所以唤醒似乎是虚假的。 线程2需要等待的条件再次。

所以既然你已经总是​​需要在一个循环下检查谓词,如果底层的条件variables可能有其他types的虚假唤醒,那就没有区别了。

pthread_cond_signal中的“条件信号的多重觉醒”部分有一个pthread_cond_wait和pthread_cond_signal的示例实现,它涉及到虚假wakekups。