2013-04-09 114 views
6

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

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

这当然是我想要发生的,当一切按预期工作时会发生什么。但有时候,我会得到以下场景:

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

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

+0

你在使用什么库?什么OS? – 2013-04-09 20:56:30

+0

使用信号量。 – 2013-04-09 21:01:07

回答

5

在线程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只做了notify_all()之前线程A获取该互斥锁或当线程A的条件变量的等待。

虽然这里的另一个关键是等待the_condition的while循环成为true。一旦A有互斥体,任何其他线程都不可能改变the_condition,直到A测试the_condition,发现它为假,并开始等待(从而释放互斥体)。

问题是:你真的在等待的是the_condition的值变成非零,std :: condition_variable :: notify_all只是告诉你线程B认为线程A应该醒来并重新测试。

+3

+1请注意'std :: condition_variable :: wait'也有一个接受谓词的重载。因此,而不是'while(the_condition == 0)cv.wait(lock);'你可以改为写'cv.wait(lock,[&] {return the_condition!= 0;});' – 2013-04-09 21:16:55

+0

@AndrewDurward: !我不知道。 – 2013-04-09 21:18:20

+1

啊,当然。可笑简单。只要锁定条件变量使用的同一个互斥量,在线程B中将条件更改为实际为真时,我已在您的代码中实现了您的建议并解决了问题。谢谢! – 2013-04-09 21:59:20

2

条件变量必须始终与互斥,以避免由一个线程准备等待和之前所述第一线程上它导致死锁实际上等待其可以用信号通知条件另一个线程创建的竞争条件相关联。线程将永远等待一个永远不会发送的信号。任何互斥体都可以使用,互斥体和条件变量之间没有明确的联系。