C++ 为什么我需要std::condition\u变量?
我发现由于虚假的唤醒,C++ 为什么我需要std::condition\u变量?,c++,c++11,concurrency,C++,C++11,Concurrency,我发现由于虚假的唤醒,std::condition_variable很难使用。因此,有时我需要设置一个标志,例如: atomic<bool> is_ready; 有什么理由我不应该这么做:(编辑:好吧,这真是个坏主意。) 如果condition\u variable选择了等待时间(我不知道这是不是真的),我宁愿自己选择。std::condition\u variable的目的是等待某个条件变为真。它并不仅仅是一个通知的接收者。例如,当使用者线程需要等待队列变为非空时,可以使用它 T
std::condition_variable
很难使用。因此,有时我需要设置一个标志,例如:
atomic<bool> is_ready;
有什么理由我不应该这么做:(编辑:好吧,这真是个坏主意。)
如果
condition\u variable
选择了等待时间(我不知道这是不是真的),我宁愿自己选择。std::condition\u variable的目的是等待某个条件变为真。它并不仅仅是一个通知的接收者。例如,当使用者线程需要等待队列变为非空时,可以使用它
T get_from_queue() {
std::unique_lock l(the_mutex);
while (the_queue.empty()) {
the_condition_variable.wait(l);
}
// the above loop is _exactly_ equivalent to the_condition_variable.wait(l, [&the_queue](){ return !the_queue.empty(); }
// now we have the mutex and the invariant (that the_queue be non-empty) is true
T retval = the_queue.top();
the_queue.pop();
return retval;
}
put_in_queue(T& v) {
std::unique_lock l(the_mutex);
the_queue.push(v);
the_condition_variable.notify_one(); // the queue is non-empty now, so wake up one of the blocked consumers (if there is one) so they can retest.
}
使用者(get\u from\u queue
)没有等待条件变量,他们正在等待条件队列.empty()
。condition变量为您提供了在他们等待时让他们进入睡眠状态的方法,同时释放互斥锁,并以一种避免错过唤醒的比赛条件的方式进行操作
您等待的条件应该由互斥锁(在等待条件变量时释放的互斥锁)保护。这意味着该条件很少(如果有)需要是原子的。您总是从互斥锁中访问它。您可以通过以下两种方式进行编码:
条件变量
std::condition_variable
的实现非常糟糕,那么它可能与轮询循环一样低效。然而,在实践中,这样的供应商应该很快停业
更新
对于露齿而笑,我用一个虚假的唤醒检测器增加了我的条件变量等待循环。我再次运行它,它没有打印出任何内容。没有一次虚假的觉醒。这当然不能保证。但它确实证明了高质量的实现可以实现什么。我已经测试了您的代码,您的说法是正确的。但我想这是因为你的杯子碰巧闲置了。我已将
此线程::yield()
替换为此线程::sleep\u(chrono::millides(1))
,该过程也占用了0.0%的cpu。@Mike:但现在您平均要等待0.5毫秒(~100万个机器周期!),您的消费者才会意识到“准备就绪”现在是真的。使用condition\u变量,您不会消耗cpu,并且可能会在几千个机器周期内收到唤醒和检查条件的通知。@Alex:如果main
和test
实际上处于不同的线程中,test
必须在修改是否准备就绪之前锁定互斥锁。@bRadGibson:Yes。第二个示例显然具有低能耗的优点,要实现这一点,您需要互斥
和条件变量
。由于这些都是必需的,因此可以在互斥锁下自由操作是否准备就绪
。只有在有明显好处的情况下,才应该拔出称为原子的锋利而危险的刀。在进行代码审查时,当您看到原子代码时,是时候喝杯新鲜咖啡,真正专注于代码了。花一小时而不是5分钟。如果您不需要原子变量
,请不要让您的代码审阅者付出额外的努力。这个答案是试验std::condition\u变量
的一个很好的起点。我很少会因为新概念而感到如此痛苦,但条件变量的概念是我一直想要的。我很高兴看到可以直接通过std
实现;如果notify调用是在锁定互斥锁的情况下完成的,那么简单的bool
就足够了。
while(!is_ready) {
this_thread::wait_for(some_duration); //Edit: changed from this_thread::yield();
}
T get_from_queue() {
std::unique_lock l(the_mutex);
while (the_queue.empty()) {
the_condition_variable.wait(l);
}
// the above loop is _exactly_ equivalent to the_condition_variable.wait(l, [&the_queue](){ return !the_queue.empty(); }
// now we have the mutex and the invariant (that the_queue be non-empty) is true
T retval = the_queue.top();
the_queue.pop();
return retval;
}
put_in_queue(T& v) {
std::unique_lock l(the_mutex);
the_queue.push(v);
the_condition_variable.notify_one(); // the queue is non-empty now, so wake up one of the blocked consumers (if there is one) so they can retest.
}
#include <atomic>
#include <chrono>
#include <iostream>
#include <thread>
std::atomic<bool> is_ready(false);
void
test()
{
std::this_thread::sleep_for(std::chrono::seconds(30));
is_ready.store(true);
}
int
main()
{
std::thread t(test);
while (!is_ready.load())
std::this_thread::yield();
t.join();
}
#include <chrono>
#include <condition_variable>
#include <iostream>
#include <mutex>
#include <thread>
bool is_ready(false);
std::mutex m;
std::condition_variable cv;
void
test()
{
std::this_thread::sleep_for(std::chrono::seconds(30));
std::unique_lock<std::mutex> lk(m);
is_ready = true;
cv.notify_one();
}
int
main()
{
std::thread t(test);
std::unique_lock<std::mutex> lk(m);
while (!is_ready)
{
cv.wait(lk);
if (!is_ready)
std::cout << "Spurious wake up!\n";
}
t.join();
}