std::condition_variable 在来自其他线程的 std::condition_variable::notify_all() 之后未正确唤醒
std::condition_variable not properly wakes up after std::condition_variable::notify_all() from other thread
此代码是对实际项目代码的简化。主线程创建工作线程并使用 std::condition_variable 等待工作线程真正启动。在下面的代码中,std::condition_variable 在 current_thread_state 变为 "ThreadState::Stopping" 后唤醒 - 这是来自工作线程的第二次通知,即主线程在第一次通知后没有唤醒,当 current_thread_state 变成 "ThreadState::Starting"。结果陷入僵局。为什么会这样?为什么 std::condition_variable 在第一个 thread_event.notify_all() 之后没有醒来?
int main()
{
std::thread thread_var;
struct ThreadState {
enum Type { Stopped, Started, Stopping };
};
ThreadState::Type current_thread_state = ThreadState::Stopped;
std::mutex thread_mutex;
std::condition_variable thread_event;
while (true) {
{
std::unique_lock<std::mutex> lck(thread_mutex);
thread_var = std::move(std::thread([&]() {
{
std::unique_lock<std::mutex> lck(thread_mutex);
cout << "ThreadFunction() - step 1\n";
current_thread_state = ThreadState::Started;
}
thread_event.notify_all();
// This code need to disable output to console (simulate some work).
cout.setstate(std::ios::failbit);
cout << "ThreadFunction() - step 1 -> step 2\n";
cout.clear();
{
std::unique_lock<std::mutex> lck(thread_mutex);
cout << "ThreadFunction() - step 2\n";
current_thread_state = ThreadState::Stopping;
}
thread_event.notify_all();
}));
while (current_thread_state != ThreadState::Started) {
thread_event.wait(lck);
}
}
if (thread_var.joinable()) {
thread_var.join();
current_thread_state = ThreadState::Stopped;
}
}
return 0;
}
一旦您调用 notify_all
方法,您的主线程和工作线程(在完成其工作后)都会尝试锁定 thread_mutex
互斥量。如果您的工作负载很小,就像在您的示例中一样,工作线程可能会在主线程之前获得锁定,并在主线程读取它之前将状态设置回 ThreadState::Stopped
。这会导致死锁。
尝试增加大量的工作量,例如
std::this_thread::sleep_for( std::chrono::seconds( 1 ) );
到工作线程。现在死锁的可能性要小得多。当然,这不能解决您的问题。这只是为了说明问题。
您有两个线程在竞争:一个写入 current_thread_state
的值两次,另一个读取 current_thread_state
的值一次。
不确定事件的顺序是写-写-读还是写-读-写,两者都是您的应用程序的有效执行。
此代码是对实际项目代码的简化。主线程创建工作线程并使用 std::condition_variable 等待工作线程真正启动。在下面的代码中,std::condition_variable 在 current_thread_state 变为 "ThreadState::Stopping" 后唤醒 - 这是来自工作线程的第二次通知,即主线程在第一次通知后没有唤醒,当 current_thread_state 变成 "ThreadState::Starting"。结果陷入僵局。为什么会这样?为什么 std::condition_variable 在第一个 thread_event.notify_all() 之后没有醒来?
int main()
{
std::thread thread_var;
struct ThreadState {
enum Type { Stopped, Started, Stopping };
};
ThreadState::Type current_thread_state = ThreadState::Stopped;
std::mutex thread_mutex;
std::condition_variable thread_event;
while (true) {
{
std::unique_lock<std::mutex> lck(thread_mutex);
thread_var = std::move(std::thread([&]() {
{
std::unique_lock<std::mutex> lck(thread_mutex);
cout << "ThreadFunction() - step 1\n";
current_thread_state = ThreadState::Started;
}
thread_event.notify_all();
// This code need to disable output to console (simulate some work).
cout.setstate(std::ios::failbit);
cout << "ThreadFunction() - step 1 -> step 2\n";
cout.clear();
{
std::unique_lock<std::mutex> lck(thread_mutex);
cout << "ThreadFunction() - step 2\n";
current_thread_state = ThreadState::Stopping;
}
thread_event.notify_all();
}));
while (current_thread_state != ThreadState::Started) {
thread_event.wait(lck);
}
}
if (thread_var.joinable()) {
thread_var.join();
current_thread_state = ThreadState::Stopped;
}
}
return 0;
}
一旦您调用 notify_all
方法,您的主线程和工作线程(在完成其工作后)都会尝试锁定 thread_mutex
互斥量。如果您的工作负载很小,就像在您的示例中一样,工作线程可能会在主线程之前获得锁定,并在主线程读取它之前将状态设置回 ThreadState::Stopped
。这会导致死锁。
尝试增加大量的工作量,例如
std::this_thread::sleep_for( std::chrono::seconds( 1 ) );
到工作线程。现在死锁的可能性要小得多。当然,这不能解决您的问题。这只是为了说明问题。
您有两个线程在竞争:一个写入 current_thread_state
的值两次,另一个读取 current_thread_state
的值一次。
不确定事件的顺序是写-写-读还是写-读-写,两者都是您的应用程序的有效执行。