condition_variable的常见用例场景

时间:2012-06-06 21:42:38

标签: c++ multithreading synchronization c++11

条件变量可用于向其他线程发出信号:

mutex m;
condition_variable cv;

thread t1([&cv]{
    // processing
    ...
    cv.notify_one();
});
...
unique_lock<std::mutex> lck(m);
cv.wait(lck);

但正如您所看到的,在我们等待通知之前,有一个机会窗口可以完成线程处理并且通知正在经过,所以我们将永远等待。

在这种情况下,常见的解决方案是使用标志:

mutex m;
condition_variable cv;
bool done = false;

thread t1([&cv,&done]{
    // processing
    ...
    done = true; 
    cv.notify_one();
});
...
unique_lock<std::mutex> lck(m);
cv.wait(lck, [&done]{return done;});

使用标志是使用condition_variable的常用方法,还是我的解释错了?

2 个答案:

答案 0 :(得分:8)

条件变量应始终与某些条件相关联,您应该测试它:

    unique_lock<mutex> lck(m);
    while (!something) 
      cv.wait(lck);

在保持互斥锁时检查条件,这意味着互斥锁应该保护与条件相关的数据,因此您知道它在测试和等待之间不会发生变化。

测试是while而不仅仅是if因为某些条件变量实现(包括基于pthreads的实现)可能会虚假地唤醒,即当没有人发出信号时,所以你应该检查一下循环并重新等待,但事实并非如此。有wait的重载采用谓词并通过等待谓词返回true来自动处理虚假唤醒,例如以上示例修改为使用检查条件的lambda:

    unique_lock<mutex> lck(m);
    cv.wait(lck, [&] { return something; });

(在简单的情况下,我发现明确的while循环更容易阅读。)

正在使用的条件变量可以被认为是由条件变量,互斥体和谓词组成的3元组,它们通过一起使用来等待条件变量在概念上绑定在一起。特定条件变量对象上的所有并发等待必须使用相同的互斥锁,并且通常也会等待相同的谓词(或依赖于相同数据的相关谓词,受同一个互斥锁保护。)

答案 1 :(得分:1)

通常condition_variable变量用于一个线程检测到它无法继续并且决定等到某个条件(在英语意义上)得到满足的情况。 condition_variable本身不是通知线程应该继续进行的主要机制,而是如果任何线程正在等待,它应该重新检查,因为状态可能已经改变,现在可以继续进行。

最简单的例子之一是生产者/消费者队列,其中消费者将拥有如下代码:

void consume() {
   empty.wait( [&] { return !queue.empty(); } );
   // extract data from the queue and consume it here
}

也就是说,线程不只是等待condition_variable变量,而是等到对象的状态使得线程可以继续。同样,通知condition_variable并没有告诉另一个线程继续,只是通知任何线程等待条件被重新测试,因为状态可能已经改变。

回到你的用例,如果你的线程需要满足的条件是另一个线程是done,那么使用这样的标志就完全没了问题