8

我的代码中存在与使用条件变量相关的死锁问题。这更像是一个设计问题,而不是纯代码问题。一旦我理解了正确的设计,我实际上编写代码就没有问题了。我有以下情况:

  1. 线程 A 等待一个条件变量。
  2. 线程 B 调用 notify_all,线程 A 被唤醒。

这当然是我想要发生的事情,也是当一切按预期工作时会发生的事情。但有时,我会得到以下情况:

  1. 线程 A 在开始等待条件变量之前执行代码。
  2. 线程 B 调用 notify_all,认为线程 A 正在等待。
  3. 线程 A 开始等待条件变量,没有意识到线程 B 已经告诉它停止等待。僵局。

解决此问题的最佳方法是什么?我想不出一种可靠的方法来检查线程 A 是否真的在等待,以便知道我什么时候应该在线程 B 中调用 notify_all。我必须求助于 timed_lock 吗?我不愿意。

4

2 回答 2

9

在线程 A 等待条件变量之前的期间,它必须持有一个互斥锁。最简单的解决方案是确保线程 B 在调用 notify_all 时持有相同的互斥锁。所以是这样的:

std::mutex m;
std::condition_variable cv;
int the_condition = 0;

Thread A: {
  std::unique_lock<std::mutex> lock(m);
  do something
  while (the_condition == 0) {
    cv.wait(lock);
  }
  now the_condition != 0 and thread A has the mutex
  do something else
} // releases the mutex;

Thread B: {
  std::unique_lock<std::mutex> lock(m);
  do something that makes the_condition != 0
  cv.notify_all();
} // releases the mutex

这保证了线程 B 仅在线程 A 获取互斥体之前或在线程 A 等待条件变量时执行 notify_all()。

不过,这里的另一个关键是等待 the_condition 变为 true 的 while 循环。一旦 A 拥有了互斥锁,任何其他线程都不应该更改 the_condition,直到 A 测试了 the_condition,发现它为假,并开始等待(从而释放互斥锁)。

关键是:您真正在等待的是 the_condition 的值变为非零, std::condition_variable::notify_all 只是告诉您线程 B 认为线程 A 应该唤醒并重新测试。

于 2013-04-09T21:05:41.043 回答
3

条件变量必须始终与互斥锁相关联,以避免由一个准备等待的线程和另一个线程创建的竞争条件,该线程可能在第一个线程实际等待它之前发出条件信号,从而导致死锁。线程将永远等待一个永远不会发送的信号。可以使用任何互斥锁,互斥锁和条件变量之间没有显式链接。

于 2015-03-04T21:50:18.457 回答