在这种简单的情况下可能出现死锁吗?

Posted

技术标签:

【中文标题】在这种简单的情况下可能出现死锁吗?【英文标题】:Is deadlock possible in this simple scenario? 【发布时间】:2016-12-27 07:33:02 【问题描述】:

请看以下代码:

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,这可能吗?还是原子性与此无关? 如果可以,我应该怎么做?

谢谢。

【问题讨论】:

std::memory_order @melak47 请提供更多详细信息。我知道这是关于std::memory_order。但我不知道std::condition_variable 是如何处理内存顺序限制的。 相关:***.com/questions/8819095/… @n.m.这已经完成了。 OP询问原子存储(memor_order_seq_cst)和condition_variable::notify_all之间的排序/同步。 terminate 不是原子时,此代码按预期工作。使其原子化是多余且浪费的。 【参考方案1】:

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

但是:

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

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

    主线程(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();

【讨论】:

无论如何,OP 应该在等待之后检查终止变量的状态。在提供的代码中,我们无法区分为什么通知条件变量。它可以是终止请求或与另一个线程的正常同步。 @paweldac:是的,他可能应该(不知道业务逻辑很难说),但这与是否会发生死锁的问题有什么关系? 非常感谢。我发现在链接[en.cppreference.com/w/cpp/thread/condition_variable]中描述了“即使共享变量是原子的,也必须在互斥锁下对其进行修改,才能正确地将修改发布到等待线程。”是关于你刚才描述的吗?或者是一个不同的故事?而且,使terminate 原子化是否毫无意义?也就是说,我可以只使用bool,不是吗?

以上是关于在这种简单的情况下可能出现死锁吗?的主要内容,如果未能解决你的问题,请参考以下文章

为啥会出现死锁?

了解特定类型的死锁

Java面试问题,如何避免Java线程中的死锁?

在这种情况下如何获得死锁?

简单的死锁示例

Jvisualvm简单使用教程