10

我今天在Boost 线程文档中遇到了这个有趣的段落:

void wait(boost::unique_lock<boost::mutex>& lock)

...

效果:原子调用 lock.unlock() 并阻塞当前线程。当调用 this->notify_one() 或 this->notify_all() 或spuriously时,线程将解除阻塞。当线程被解除阻塞时(无论出于何种原因),在调用等待返回之前调用 lock.lock() 重新获取锁。如果函数因异常退出,还可以通过调用 lock.lock() 重新获取锁。

所以我感兴趣的是“spuriously”这个词的含义。为什么线程会因为虚假原因而被解除阻塞?可以做些什么来解决这个问题?

4

2 回答 2

11

Anthony Williams 的这篇文章特别详细。

无法预测虚假尾流:从用户的角度来看,它们基本上是随机的。但是,当线程库无法可靠地确保等待线程不会错过通知时,它们通常会发生。由于错过的通知会使条件变量变得无用,线程库将线程从等待中唤醒,而不是冒险。

他还指出,您不应该使用timed_wait需要持续时间的重载,您通常应该使用带有谓词的版本

这是初学者的错误,并且可以通过一个简单的规则轻松克服:在等待条件变量时始终在循环中检查谓词。更隐蔽的错误来自 timed_wait()。

Vladimir Prus 的这篇文章也很有趣。

但是为什么我们需要while循环,我们不能写:

if (!something_happened)
  c.wait(m);

我们不能。杀手的原因是“等待”可以在没有任何“通知”调用的情况下返回。这称为虚假唤醒,POSIX 明确允许。本质上,从“等待”返回仅表示共享数据可能已更改,因此必须再次评估数据。

好的,那为什么还没有修复呢?第一个原因是没有人愿意修复它。由于其他几个原因,非常希望在循环中包装对“等待”的调用。但是这些原因需要解释,而虚假唤醒是一把锤子,可以毫无疑问地适用于任何一年级学生。

于 2009-03-09T08:14:43.830 回答
2

这篇博文给出了使用 Linux 的原因,即futex当信号传递到进程时系统调用返回。不幸的是,它没有解释任何其他内容(实际上是在要求更多信息)。

关于虚假唤醒的维基百科条目(这似乎是一个 posix 范围的概念,顺便说一句,不限于提升)可能你也感兴趣。

于 2009-03-09T08:14:04.330 回答