在这个简单的场景中是否可能出现死锁?
Is deadlock possible in this simple scenario?
请看下面代码:
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
,这是否可能?或者原子性与此无关?
- 如果可以的话,我应该怎么做?
谢谢。
我不相信,你描述的是可能的,因为cv.notify_all()
afaik(如果我错了请纠正我)与wait()
同步,所以当工作线程唤醒时,它将看到 terminate
.
的变化
然而:
死锁可以通过以下方式发生:
工作线程 (WT) 确定 terminate
标志仍然为假。
主线程(MT)设置terminate
标志并调用cv.notify_all()
.
- 因为目前没有人在等待通知获得的条件变量"lost/ignored"。
- MT 调用
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();
}
请看下面代码:
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
,这是否可能?或者原子性与此无关? - 如果可以的话,我应该怎么做?
谢谢。
我不相信,你描述的是可能的,因为cv.notify_all()
afaik(如果我错了请纠正我)与wait()
同步,所以当工作线程唤醒时,它将看到 terminate
.
然而:
死锁可以通过以下方式发生:
工作线程 (WT) 确定
terminate
标志仍然为假。主线程(MT)设置
terminate
标志并调用cv.notify_all()
.- 因为目前没有人在等待通知获得的条件变量"lost/ignored"。
- MT 调用
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();
}