1

尝试在我之前的两个问题中扩展以线程作为成员变量的类的移动操作传递给线程的 lambda 内的调用函数

我不明白为什么执行 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;
}
4

1 回答 1

1

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

{
  std::unique_lock<std::mutex> lock(mtx);
  ready = true;
}
于 2016-07-28T13:57:59.707 回答