为什么我需要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_variable
select了等待时间(我不知道这是否是真的),我宁愿自己select它。
你可以这样编码:
- 使用primefaces和轮询循环。
- 使用
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
。 你总是从一个互斥体中访问它。