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

时间:2016-07-28 12:42:31

标签: c++ multithreading c++11 gcc4.8

尝试扩展前两个问题Move operations for a class with a thread as member variableCall function inside a lambda passed to a thread

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

上说
  

通知线程不需要将锁定保持在与等待线程所持有的互斥锁相同的互斥锁上;实际上这样做是一种悲观,因为通知的线程会立即再次阻塞,等待通知线程释放锁。

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;
}

1 个答案:

答案 0 :(得分:1)

您需要一个内存屏障,以确保其他线程将看到修改后的“就绪”值。 “ready”是原子的,只能确保对内存访问进行排序,以便在原子访问之前发生的修改实际上被刷新到主内存。这并不能保证其他线程会看到那个内存,因为这些线程可能有自己的内存缓存。因此,为了确保其他线程看到“就绪”修改,需要使用互斥锁。

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