为什么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。