4

条件变量可用于向其他线程发出信号表明发生了某些事情:

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;});

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

4

2 回答 2

7

条件变量应始终与某些条件相关联,您应该对其进行测试:

    unique_lock<mutex> lck(m);
    而(!某事)
      cv.wait(lck);

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

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

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

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

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

于 2012-06-07T01:07:54.960 回答
0

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

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

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

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

回到你的用例,如果你的线程需要满足的条件done是另一个线程是,那么使用这样的标志是非常好的。

于 2012-06-06T22:15:49.327 回答