std::condition_variable 在 std::condition_variable::notify_all() 从其他线程后未正确唤醒
Posted
技术标签:
【中文标题】std::condition_variable 在 std::condition_variable::notify_all() 从其他线程后未正确唤醒【英文标题】:std::condition_variable not properly wakes up after std::condition_variable::notify_all() from other thread 【发布时间】:2015-08-03 14:06:38 【问题描述】:此代码是实际项目代码的简化。主线程创建工作线程并使用 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;
【问题讨论】:
【参考方案1】:一旦你调用了notify_all
方法,你的主线程和你的工作线程(在完成它的工作之后)都试图获得thread_mutex
互斥锁上的锁。如果您的工作负载微不足道,例如在您的示例中,工作线程可能会在主线程之前获得锁,并在主线程读取它之前将状态设置回ThreadState::Stopped
。这会导致死锁。
尝试增加大量工作负载,例如
std::this_thread::sleep_for( std::chrono::seconds( 1 ) );
到工作线程。现在死锁的可能性要小得多。当然,这不能解决您的问题。这只是为了说明问题。
【讨论】:
是的。你说得对。如果在工作线程 func 中注释 cout.setstate(std::ios::failbit),那么一切正常。但我不完全明白,为什么 thread_event.wait(lck) 在第一个“thread_event.notify_all()”之后没有醒来,因为 c++ 文档说:等待操作原子地释放互斥锁并暂停线程的执行。当通知条件变量时,线程被唤醒,并重新获取互斥体。 所以在这种情况下最简单的解决方案是将while (current_thread_state != ThreadState::Started)
更改为while (current_thread_state == ThreadState::Stopped)
@stefaanv,一定要找到状态ThreadState::Started,保证知道工作线程正在运行。第一次和第二次调用 thread_event.notify_all() 之间的工作线程中的代码可以执行任意时间或等待某个事件。
@soler:好的,我是针对发布的问题提出解决方案,而不是您的要求。无论如何,问题是你不能保证你能够检查确切的状态,所以你必须以某种方式处理它..
@Soler:关于你在第一条评论中的问题:是的,互斥体是获得的,基本上就像你会在它上面调用lock
方法一样。但是在试图获取互斥锁的两个线程之间存在竞争条件。哪个线程获得锁是未定义的。关于您的问题,您可以使用第二个condition_variable
或使用轮询。但从更高的角度分析您的实际需求时,可能会有更好的解决方案。【参考方案2】:
您有两个线程在竞争:一个写入两次current_thread_state
的值,另一个读取一次current_thread_state
的值。
不确定事件顺序是写-写-读还是写-读-写,两者都是您的应用程序的有效执行。
【讨论】:
以上是关于std::condition_variable 在 std::condition_variable::notify_all() 从其他线程后未正确唤醒的主要内容,如果未能解决你的问题,请参考以下文章
std::atomic 和 std::condition_variable 等待、notify_* 方法之间的区别
C++ std::condition_variable wait() wait_for() 区别 怎么用 实例
在等待std :: condition_variable时如何处理系统时钟更改?
排序多个线程如何在 std::condition_variable::notify_all 之后重新获取 std::unique_lock<std::mutex>