在这种简单的情况下可能出现死锁吗?
Posted
技术标签:
【中文标题】在这种简单的情况下可能出现死锁吗?【英文标题】:Is deadlock possible in this simple scenario? 【发布时间】:2016-12-27 07:33:02 【问题描述】:请看以下代码:
std::mutex mutex;
std::condition_variable cv;
std::atomic<bool> terminate;
// Worker thread routine
void work()
while( !terminate )
std::unique_lock<std::mutex> lg mutex ;
cv.wait(lg);
// Do something
// Do something
// This function is called from the main thread
void terminate_worker()
terminate = true;
cv.notify_all();
worker_thread.join();
是否会发生以下情况?
-
工作线程正在等待信号。
主线程调用
terminate_worker()
;
主线程将原子变量terminate
设置为true
,然后向工作线程发出信号。
工作线程现在唤醒,完成其工作并从terminate
加载。在这一步,主线程对terminate
的更改还没有看到,所以工作线程决定等待另一个信号。
现在发生死锁...
我想知道这是否可能。据我了解,std::atomic
只保证没有竞争条件,但内存顺序是另一回事。问题:
-
这可能吗?
如果这不可能,如果
terminate
不是原子变量而只是bool
,这可能吗?还是原子性与此无关?
如果可以,我应该怎么做?
谢谢。
【问题讨论】:
std::memory_order
@melak47 请提供更多详细信息。我知道这是关于std::memory_order
。但我不知道std::condition_variable
是如何处理内存顺序限制的。
相关:***.com/questions/8819095/…
@n.m.这已经完成了。 OP询问原子存储(memor_order_seq_cst
)和condition_variable::notify_all
之间的排序/同步。
当terminate
不是原子时,此代码按预期工作。使其原子化是多余且浪费的。
【参考方案1】:
我不相信,你描述的是可能的,因为cv.notify_all()
afaik(如果我错了请纠正我)与wait()
同步,所以当工作线程唤醒时,它会看到@的变化987654324@.
但是:
死锁可以通过以下方式发生:
工作线程 (WT) 确定 terminate
标志仍然为假。
主线程(MT)设置terminate
标志并调用cv.notify_all()
。
join
并阻止。
WT 进入睡眠状态 (cv.wait()
) 并阻塞。
解决方法:
虽然您在调用 cv.notify 时不必持有锁,但您
在修改terminate
时必须持有锁(即使它是原子的)
必须确保,当您持有相同的锁时,条件检查和对wait
的实际调用发生。
这就是为什么有一种wait
的形式在将线程发送到睡眠状态之前执行此检查的原因。
更正后的代码(改动很小)可能如下所示:
// Worker thread routine
void work()
while( !terminate )
std::unique_lock<std::mutex> lg mutex ;
if (!terminate)
cv.wait(lg);
// Do something
// Do something
// This function is called from the main thread
void terminate_worker()
std::lock_guard<std::mutex> lg(mutex);
terminate = true;
cv.notify_all();
worker_thread.join();
【讨论】:
无论如何,OP 应该在等待之后检查终止变量的状态。在提供的代码中,我们无法区分为什么通知条件变量。它可以是终止请求或与另一个线程的正常同步。 @paweldac:是的,他可能应该(不知道业务逻辑很难说),但这与是否会发生死锁的问题有什么关系? 非常感谢。我发现在链接[en.cppreference.com/w/cpp/thread/condition_variable]中描述了“即使共享变量是原子的,也必须在互斥锁下对其进行修改,才能正确地将修改发布到等待线程。”是关于你刚才描述的吗?或者是一个不同的故事?而且,使terminate
原子化是否毫无意义?也就是说,我可以只使用bool
,不是吗?以上是关于在这种简单的情况下可能出现死锁吗?的主要内容,如果未能解决你的问题,请参考以下文章