请参阅以下代码:
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()
。 - 因为没有人会经常等待通知"丢失/忽略"的条件变量。
- MT调用
join
和块。 - wt入睡(
cv.wait()
),也可以阻止。
解决方案:
虽然您在致电CV时不必锁定锁。您
- 在修改
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();
}