为什么我需要std :: condition_variable?

我发现std::condition_variable由于虚假唤醒而非常难以使用。 所以有时我需要设置一个标志,如:

 atomic<bool> is_ready; 

我在调用notify( notify_one()notify_all() )之前将is_ready设置为true ,然后等待:

 some_condition_variable.wait(some_unique_lock, [&is_ready]{ return bool(is_ready); }); 

有什么理由不应该这样做:(编辑:好吧,这真是一个坏主意。)

 while(!is_ready) { this_thread::wait_for(some_duration); //Edit: changed from this_thread::yield(); } 

如果condition_variableselect了等待时间(我不知道这是否是真的),我宁愿自己select它。

你可以这样编码:

  1. 使用primefaces和轮询循环。
  2. 使用condition_variable

我已经在下面为你编写了两种方法。 在我的系统上,我可以实时监控任何给定进程正在使用的CPU数量。

先用轮询循环:

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

对我来说,这需要30秒的时间来执行,而执行该过程需要大约99.6%的CPU。

或者用一个condition_variable

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

这有完全相同的行为,只不过在30秒执行期间,进程占用了0.0%cpu。 如果你正在编写一个可以在电池供电的设备上执行的应用程序,那么后者在电池上几乎是非常容易的。

无可否认,如果std::condition_variable执行效果很差,它可能与轮询循环具有相同的低效率。 但实际上,这样的供应商应该很快就会倒闭。

更新

对于微笑,我用假的唤醒检测器增强了我的condition_variable等待循环。 我又跑了一遍,没有打印出任何东西。 没有一个虚假的唤醒。 这当然不能保证。 但它确实certificate了一个高质量的实现可以实现。

std::condition_variable的目的是等待一些条件成立。 它不是被devise成仅仅是通知的接收者。 例如,当消费者线程需要等待队列变为非空时,您可以使用它。

 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_from_queue等待条件variables,他们正在等待条件the_queue.empty() 。 这个条件variables让你可以在等待的时候让它们睡觉,同时释放这个互斥量,这样可以避免你在起床时会遇到的竞争情况。

你正在等待的条件应该受到一个互斥体的保护(当你等待条件variables的时候你释放的那个互斥体)。这意味着条件很less(如果有的话)需要是一个atomic 。 你总是从一个互斥体中访问它。