为什么我需要std::condition_variable
why do I need 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
选择了等待时间(我不知道这是真的还是假的),我宁愿自己选择。
你可以这样写:
- 使用原子和轮询循环。
- 使用
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等待循环。我又运行了一遍,没有打印出任何东西。没有一次虚假的觉醒。这当然不能保证。但它确实展示了一个高质量的实现可以实现什么。
std::condition_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_from_queue
) 不是等待条件变量,他们正在等待条件the_queue.empty()
。条件变量让你可以在它们等待的时候让它们进入睡眠状态,同时释放互斥锁,这样做可以避免在竞争条件下错过唤醒。
等待的条件应该被互斥锁保护(当等待条件变量时释放的互斥锁)。这意味着条件很少(如果有的话)需要是atomic
。
- C++ Singleton - Prevent ::instance() to variable
- 如何通过多类"Union variable" (sfml) 使用轮询事件
- 将成员函数作为构造函数参数调用时出错 "Variable is not a type name"
- 为什么我会收到"Run-Time Check Failure #2 - Stack around the variable 'pr' was corrupted"错误?
- C++:寻找"returning address of local variable..."的更正
- C++ - 在我尝试制作一个简单的计算器时有一个"uninitialized local variable y used"警告
- Visual Studio Code "variable " u8 的 C/C++ 扩展名 " " 不是类型名称"
- 我需要std::condition,但有两个以上的选择
- C++ "Using Uninitialized Memory.. (variable name) "
- cudaMallocManaged for host-initiated variable
- Cppcheck "Reference to auto variable returned"失败的原因?
- OpenCV 和 C++:"Can't resolve variable 'Mat`"
- 是否有用于"go to variable type definition" C++中"auto"变量的 Visual Studio 键盘快捷方式?
- 冲刺错误"variable may be unsafe" (C4996)...选择?
- Visual Studio生成"Error: uninitialized local variable 'x' "而在线编译器不会 - 为什么?
- GUID variable for JPEG,BMP,PNG
- 如何修复初始化后'stack around variable was corrupted.'变量未更改为分配的值
- C++:从 "const variable*" 转换为"uint32"会失去精度
- C++17 'inline variable' vs 'extern' 关键字继承自 C 的新功能
- Tensorflow C++ API assign variable value