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

标签 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,然后向工作线程发出信号。
    • 工作线程现在唤醒,执行其工作并从终止加载。在这一步, 终止 主线程所做的更改还没有看到,因此工作线程决定等待另一个信号。
  3. 现在发生死锁...

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

  1. 这可能吗?
  2. 如果这不可能,如果 terminate 不是原子变量而只是 bool,这是否可能?或者原子性与此无关?
  3. 如果可能,我应该怎么做?

谢谢。

最佳答案

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

但是:

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

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

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

  3. 因为目前没有人在等待通知“丢失/忽略”的条件变量。
  4. MT 调用join 和 block 。
  5. WT 进入休眠状态(cv.wait())并阻塞。

解决方案:

虽然您在调用 cv.notify 时不必持有锁,但您

  • 在修改 terminate 时必须持有锁(即使它是原子的)
  • 必须确保,条件检查和对 wait 的实际调用发生在您持有同一个锁的情况下。

这就是为什么有一种 wait 的形式,它在将线程发送到 sleep 状态之前执行此检查。

更正后的代码(改动很小)可能如下所示:

// 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/

相关文章:

vulkan - vulkan 管道内存屏障是否减轻了与没有内存屏障的管道屏障相关的同步约束?

c++ - 如何从具有指针的结构数组中删除元素?

java - 在 Callable 中处理 Thread.interrupted() 的正确方法?

c++ - 对同一原子变量混合放松访问和获取/释放访问如何影响同步?

Android:ContentResolver 实例是线程安全的吗?

multithreading - gcc参数: -pthread.它是做什么的?

c# - 关于 OoOP 的 Thread.MemoryBarrier() 错误的解释

C++ ifstream/fstream 破坏数据

java - 在 Java 中读取 C++ 二进制文件

c++ - 从 C++ 中的函数 typedef 制作函数指针 typedef?