等待条件下的C ++线程死锁

C++ thread deadlock on waiting condition

尝试扩展我之前的两个问题 and

我不明白为什么执行 wait_for 的线程有时没有收到通知导致死锁。 Cppreference 表示条件变量 http://en.cppreference.com/w/cpp/thread/condition_variable/notify_one

The notifying thread does not need to hold the lock on the same mutex as the one held by the waiting thread(s); in fact doing so is a pessimization, since the notified thread would immediately block again, waiting for the notifying thread to release the lock.

MCVE,注释行解释了如果我持有锁会发生什么变化,但我不明白为什么:

#include <atomic>
#include <condition_variable>
#include <mutex>
#include <thread>

#include <iostream>

using namespace std;

class worker {
public:
    template <class Fn, class... Args>
    explicit worker(Fn func, Args... args) {
        t = std::thread(
            [&func, this](Args... cargs) -> void {
                std::unique_lock<std::mutex> lock(mtx);
                while (true) {
                    cond.wait(lock, [this]() -> bool { return ready; });
                    if (terminate) {
                        break;
                    }

                    func(cargs...);
                    ready = false;
                }
            },
            std::move(args)...);
    }

    ~worker() {
        terminate = true;
        if (t.joinable()) {
            run_once();
            t.join();
        }
    }

    void run_once() {
        // If i dont hold this mutex the thread is never notified of ready being
        // true.
        std::unique_lock<std::mutex> lock(mtx);
        ready = true;
        cout << "ready run once " << ready << endl;
        cond.notify_all();
    }

    bool done() { return (!ready.load()); }

private:
    std::thread t;
    std::atomic<bool> terminate{false};
    std::atomic<bool> ready{false};
    std::mutex mtx;
    std::condition_variable cond;
};

// main.cpp

void foo() {
    worker t([]() -> void { cout << "Bark" << endl; });
    t.run_once();
    while (!t.done()) {
    }
}

int main() {
    while (true) {
        foo();
    }
    return 0;
}

您需要一个内存屏障来确保其他线程将看到修改后的 "ready" 值。 "ready" 是原子的只确保内存访问是有序的,以便在原子访问之前发生的修改实际上被刷新到主内存。这并不能保证其他线程会看到该内存,因为这些线程可能有自己的内存缓存。因此,要确保其他线程看到 "ready" 修改,需要互斥量。

{
  std::unique_lock<std::mutex> lock(mtx);
  ready = true;
}