条件变量死锁

Condition variable deadlock

本文关键字:死锁 变量 条件      更新时间:2023-10-16

我的代码中有一个死锁问题,与使用条件变量有关。这更像是一个设计问题,而不是纯粹的代码问题。一旦我理解了正确的设计,我写代码就没有问题了。我有以下场景:

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

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

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

解决这个问题的最好方法是什么?我想不出一种可靠的方法来检查线程a是否实际上正在等待,以便知道何时应该在线程b中调用notify_all。我必须求助于time_lock吗?我不愿意。

在线程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为真的while循环。一旦A线程有了互斥锁,其他线程就不可能改变这个条件,除非A线程测试了这个条件,发现它为假,并开始等待(从而释放互斥锁)。

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

条件变量必须始终与互斥锁相关联,以避免一个线程准备等待而另一个线程可能在第一个线程实际等待之前发出条件信号而导致死锁。线程将永远等待一个永远不会发送的信号。任何互斥锁都可以使用,互斥锁和条件变量之间没有明确的联系。