请看以下代码:

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 ,然后向工作线程发出信号。
  • Worker 线程现在唤醒,执行其工作并从 terminate 加载。这一步还没有看到主线程对terminate的改变,所以工作线程决定等待另一个信号。
  • 现在发生死锁...

  • 我想知道这是可能的。据我了解,std::atomic 只保证没有竞争条件,但内存顺序是另一回事。问题:
  • 这可能吗?
  • 如果这不可能,如果 terminate 不是原子变量而只是 bool ,这可能吗?还是原子性与此无关?
  • 如果可以,我该怎么办?

  • 谢谢你。

    最佳答案

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

    但是:

    死锁可以通过以下方式发生:

  • 工作线程 (WT) 确定 terminate 标志仍然为 false。
  • 主线程 (MT) 设置 terminate 标志并调用 cv.notify_all()
  • 因为目前没有人在等待通知“丢失/忽略”的条件变量。
  • 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();
    }
    

    关于c++ - 在这个简单的场景中是否可能出现死锁?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/41340960/

    10-11 00:18