在这个简单的场景中是否可能出现死锁?

时间:2016-12-27 07:33:02

标签: c++ multithreading memory-barriers

请参阅以下代码:

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. 现在发生死锁......
  4. 我想知道这是可能的。据我所知,std::atomic只保证没有竞争条件,但记忆顺序是另一回事。问题:

    1. 这可能吗?
    2. 如果无法做到这一点,如果terminate不是原子变量而只是bool,这是否可行?或者原子性与此无关?
    3. 如果可以,我该怎么办?
    4. 谢谢。

1 个答案:

答案 0 :(得分:2)

我不相信,你所描述的是可能的,cv.notify_all() afaik(如果我错了请纠正我)与wait()同步,所以当工作线程醒来时,它会看到对terminate的更改。

<强>然而

死锁可能发生在以下方面:

  1. 工作线程(WT)确定terminate标志仍为假。

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

  3. 由于没有人正在等待条件变量,通知会被“丢失/忽略”。
  4. MT调用join并阻止。
  5. WT进入睡眠状态(cv.wait())并阻止。
  6. 解决方案:

    虽然您在调用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();
    }