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

C++ 在这个简单的场景中是否可能出现死锁?,c++,multithreading,memory-barriers,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);

请参阅以下代码:

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();
}
std::mutex mutex;
std::条件变量cv;
std::原子终止;
//工作线程例程
无效工作(){
而(!终止){
{
std::unique_lock lg{mutex};
cv.wait(lg);
//做点什么
}
//做点什么
}
}
//此函数是从主线程调用的
无效终止工作人员(){
终止=真;
cv.通知所有人();
worker_thread.join();
}
是否会发生以下情况

  • 工作线程正在等待信号
  • 主线程名为
    terminate_worker()
    • 主线程将原子变量
      terminate
      设置为
      true
      ,然后向工作线程发送信号
    • 工作线程现在唤醒,执行其工作并从
      终止加载。在这一步中,还看不到主线程对
      terminate
      所做的更改,因此工作线程决定等待另一个信号
  • 现在死锁发生了
  • 我想知道这是否可能。据我所知,
    std::atomic
    只保证没有竞争条件,但内存顺序是另一回事。问题:

  • 这可能吗
  • 如果这是不可能的,那么如果
    terminate
    不是一个原子变量,而只是
    bool
    ,这是可能的吗?或者原子性与此无关
  • 如果可能的话,我该怎么办

  • 谢谢。

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

    但是:

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

  • 工作线程(WT)确定
    终止
    标志仍然为false

  • 主线程(MT)设置
    terminate
    标志并调用
    cv.notify\u all()

  • 因为现在没有人在等待条件变量,所以通知会“丢失/忽略”
  • MT调用
    连接
    和块
  • WT进入睡眠状态(
    cv.wait()
    )并阻塞 解决方案:

    当你打电话给cv.notify时,你不必持有锁,但是

    • 在修改
      终止时必须持有锁(即使是原子锁)
    • 必须确保在您持有相同的锁时,对条件进行检查并实际调用
      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::unique_lock lg{mutex};
    如果(!终止){
    cv.wait(lg);
    }
    //做点什么
    }
    //做点什么
    }
    }
    //此函数是从主线程调用的
    无效终止工作人员(){
    {
    标准:锁紧保护lg(互斥);
    终止=真;
    }
    cv.通知所有人();
    worker_thread.join();
    }
    
    @melak47请提供更多详细信息。我知道这是关于
    std::memory\u order
    。但是我不知道
    std::condition_variable
    如何与内存顺序限制一起工作。相关:@n.m.这已经完成了。OP询问原子存储(使用
    memororder\u seq\u cst
    )和
    condition\u变量::notify\u all
    )之间的排序/同步。当
    terminate
    不是原子时,此代码按预期工作。使其原子化是冗余和浪费的。在任何情况下,OP都应该在等待后检查terminate变量的状态。在提供的代码中,我们无法区分通知条件变量的原因。它可以是terminete请求,也可以是与另一个线程的正常同步。@paweldac:是的,他可能应该(在不知道业务逻辑的情况下,很难说),但这与是否会发生死锁的问题有什么关系?非常感谢。我发现在链接中[它被描述为“即使共享变量是原子的,也必须在互斥下修改它,以便正确地将修改发布到等待的线程。”这是关于你刚才描述的吗?还是一个不同的故事?还有,使
    终止
    原子化是否毫无意义?也就是说,我可以使用
    bool
    ,不是吗?