在这种简单的情况下,可能是僵局



请参阅以下代码:

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();
}

可以发生以下情况吗?

  1. 工作线程正在等待信号。
  2. 主线程称为terminate_worker();
    • 主线程将原子变量terminate设置为true,然后发出信号。
    • 工作线现在醒来,从terminate努力工作并加载。在此步骤中,尚未看到主线程 terminate 的更改,因此工作线程决定等待另一个信号。
  3. 现在发生僵局...

我想知道这是可能的。据我了解,std::atomic仅保证没有种族条件,但是内存顺序是另一回事。问题:

  1. 这是可能的吗?
  2. 如果这是不可能的,那么如果terminate不是原子变量,而只是bool,这是否可以?还是原子与此无关?
  3. 如果可能的话,我该怎么办?

谢谢。

我不相信,您所描述的是可能的,因为cv.notify_all() afaik(如果我错了,请纠正我)与wait()同步,因此,当工作人员线程醒来时,它将看到更改为terminate

但是:

僵局可能以下方式发生:

  1. 工作线程(WT)确定terminate标志仍然是错误的。

  2. 主线程(MT)设置terminate标志并调用cv.notify_all()

  3. 因为没有人会经常等待通知"丢失/忽略"的条件变量。
  4. MT调用join和块。
  5. 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();
}

最新更新