【发布时间】:2021-01-02 16:38:15
【问题描述】:
我遇到了这种有趣的虚假唤醒行为。考虑这个简单的演示代码:
#include <iostream>
#include <chrono>
#include <thread>
#include <condition_variable>
#include <mutex>
using namespace std; // I know
using namespace std::chrono;
using namespace std::chrono_literals;
mutex mtx; // used for cv and synchronized access to counter
condition_variable cv;
int counter = 0; // (1)
bool keep_running = true; // flag for signaling an exit condition
int main()
{
// thread that decrements counter every time it is woken up and the counter is > 0
thread t([&] {
while (keep_running)
{
unique_lock<mutex> lock(mtx);
cv.wait(lock, [&] {
cout << "Woken up" << endl;
return !keep_running || counter > 0;
});
if (!keep_running) { // check for exit condition
break;
}
--counter;
}
});
this_thread::sleep_for(1s); // some work
unique_lock<mutex> lock(mtx);
counter = 5; // set the counter
cout << "Notifying" << endl;
lock.unlock();
cv.notify_one(); // wake the thread up
this_thread::sleep_for(1s); // some more work
cout << "Exiting" << endl;
lock.lock();
keep_running = false; // ask the thread to exit
lock.unlock();
cv.notify_one(); // wake up one last time
t.join(); // join and exit
cout << "Counter: " << counter << endl;
}
使用g++ cv_test.cpp -o cv_test -pthread 编译并执行会产生以下输出:
Woken up
Notifying
Woken up
Woken up
Woken up
Woken up
Woken up
Woken up
Exiting
Woken up
Counter: 0
请注意,我只调用了一次notify_one,但线程会不断被唤醒,直到谓词返回 false。无论计数器初始化为什么,线程都会被唤醒,直到它变为 0(即谓词)。
即使在执行开始时,线程也会被唤醒一次,好像是为了“检查”谓词是否返回 false。因此,如果我将计数器初始化为正值:int counter = 3; // (1),则虚假唤醒似乎“确保”谓词在调用第一个 notify_one 之前返回 false。
我的问题是,这真的是一个功能吗?可以依赖它吗?有没有关于这种行为的文档?
PS。我知道在等待 condition_variable 之前,可以通过简单检查计数器(读取:工作队列长度)来修复这个工作线程,但是这种虚假唤醒的可预测行为引起了我的兴趣。
【问题讨论】:
-
这是条件变量的好读物:modernescpp.com/index.php/…
标签: c++ multithreading condition-variable