虚假的唤醒说明听起来像是一个不值得修复的错误,对吗?


30

根据维基百科有关虚假唤醒的文章

“即使没有线程向条件变量发出信号,线程也可能从等待状态中唤醒”。

虽然我已经知道了这个“功能”,但是直到同一篇文章中,我才知道真正的原因是什么

“虚假唤醒听起来可能很奇怪,但是在某些多处理器系统上,使条件唤醒完全可预测可能会大大减慢所有条件变量的运行。”

听起来像是一个不值得修复的错误,对吗?


1
相关:“为什么调用pthread_cond_wait有虚假唤醒?”,stackoverflow.com/questions/8594591/...
弗洛里安·卡斯特兰

Answers:


39

虚假唤醒的TL; DR假设(“合同”)是明智的体系结构决策,旨在允许对线程sheduler进行切实可行的实现。

“性能注意事项”在这里是无关紧要的,这些只是误解,由于在公开的权威参考文献中进行了说明而变得很普遍。(您知道,权威参考文献可能有误-只需问Galileo Galilei即可。Wikipedia文章保留了对您引用的注释的引用,只是因为该注释与引用正式参考的正式指南完全吻合。

SO此答案中提供了引入虚假唤醒概念的更多令人信服的理由,该理由基于该文章(旧版本)中提供的其他详细信息:

维基百科上有关虚假唤醒的文章有以下提示:

pthread_cond_wait()Linux中的功能是使用futex系统调用实现的。EINTR当进程接收到信号时,Linux上的每个阻塞系统调用都会突然返回。... pthread_cond_wait()无法重新开始等待,因为它可能会在futex系统调用之外的短时间内错过一次真正的唤醒...

试想一下……就像任何代码一样,线程调度程序可能会由于基础硬件/软件中发生的异常而经历暂时的中断。当然,应尽量避免这种情况的发生,但是由于没有100%健壮的软件之类的东西,因此可以合理地假设这种情况可能发生,并在调度程序检测到这种情况时谨慎地进行恢复(例如通过观察丢失的心跳)。

现在,考虑到在停电期间它可能会丢失一些旨在通知等待线程的信号,调度程序如何恢复?如果调度程序不执行任何操作,则提到的“不幸”线程将永远挂起,一直等待-为避免这种情况,调度程序将向所有等待的线程发送信号。

这使得必须建立一个“契约”,以便可以毫无理由地通知等待线程。确切地说,这是有原因的-调度程序中断-但由于线程(出于充分的理由)被设计为不了解调度程序内部实现的详细信息,因此,此原因可能更好地表现为“虚假的”。


从线程的角度来看,这有点类似于Postel定律(也称为稳健性原理),

对自己的工作要保守,对别人接受的事情要开放

虚假唤醒的假设迫使线程在其操作上保持保守:在通知其他线程时设置条件,而在接受的条件上保持宽松:从等待返回时检查条件,如果尚未返回则重复等待。


10
...... Postel的定律... HTML和各种Web技术被扔进垃圾桶的原因(例如HTML接受不良标签嵌套)。撇开,好答案。
Thomas Eding

3
Postel的定律是为什么许多错误会在几年内未被捕获的原因,因为嘿,即使您的函数返回错误的输出,该应用程序似乎仍然可以正常工作!有史以来最好的发明。
Pacerier

2
@Pacerier:返回错误输出的函数未遵循Postel定律(保守部分)。
YvesgereY

@Pacerier:OTOH,要求其他组件严格,以便可以早点发现错误是一个有趣的立场,这是基于“快速失败”原则和“基于合同”的设计。
YvesgereY

1

不值得解决,因为调用者代码无论如何都要使用相同的处理方式(检查条件),以便处理竞争条件。

一种解决两个问题的方法,我总结如下:

伪唤醒:计划在条件建立之前等待线程。
强制休眠:在条件再次被伪造后安排等待线程。

由于以后可能发生,因此有些人甚至在合同中引入了虚假唤醒:

  • 通过要求谓词循环来实施良好实践。
  • 给调度程序实现一些自由(包括@gnat指出的紧急恢复选项)。

SO参考


我想为此+1,但出于这样的想法,有人故意引入虚假唤醒,以使调用者添加谓词循环来解决强制性的过度睡眠。我觉得这简直不可思议。
鲁阿赫

“其目的是通过要求谓词循环来强制正确/健壮的代码。” 请参阅提供的链接。
YvesgereY
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.