无限等待条件变量

Infinite waiting on condition variable

简化的目标是强制在3个不同的线程中一个一个调用3个成员函数(线程A调用F::first,线程BF::second,线程CF::third)。

为了实现线程的执行顺序,我使用了 1 个条件变量和 2 个布尔值来指示第一个和第二个线程是否完成了它们的工作。

代码中:

std::mutex mtx;
std::condition_variable cv;
bool firstPrinted = false;
bool secondPrinted = false;

class F {
public:    
    void first(std::function<void()> printFirst) {
        std::unique_lock<std::mutex> lck(mtx);
        std::cout << "first\n";
        printFirst();
        firstPrinted = true;
        cv.notify_one();
    }

    void second(std::function<void()> printSecond) {
        std::unique_lock<std::mutex> lck(mtx);
        std::cout << "second\n";
        cv.wait(lck, []() { return firstPrinted; });
        printSecond();
        secondPrinted = true;
        cv.notify_one();
    }

    void third(std::function<void()> printThird) {
        std::unique_lock<std::mutex> lck(mtx);
        std::cout << "third\n";
        cv.wait(lck, []() { return secondPrinted; });
        printThird();
    }
};

auto first = []() {
    std::cout << "1";
};
auto second = []() {
    std::cout << "2";
};
auto third = []() {
    std::cout << "3";
};
F f;
std::thread A(&F::first,  &f, first);
std::thread B(&F::second, &f, second);
std::thread C(&F::third,  &f, third);
A.join(); B.join(); C.join();

现在让我们考虑一下这种情况:

线程A不先启动——无论第一个启动线程是B还是C,它们都阻塞(等待)直到得到通知(B阻塞直到A通知,C阻塞直到B通知)

当第一个启动线程是 C 时出现无限等待(或者可能是死锁!?),它总是产生以下输出:

third
second
first
...and stalling here

理论上,这不应该发生,因为在线程 C 中调用 cv.wait 会解锁允许线程 B 进入 运行 的互斥量,而后者又会等待(因为条件未变为真),因此它解锁锁定的互斥锁,并允许线程 A 首先启动,最终应该进入临界区并通知 B。

  1. 导致程序卡顿的调用路径是什么?

  2. 我错过了什么细微差别?

以上思路如有错误请指正

std::condition_variable::notify_one() 将唤醒等待 condition_variable 一个 线程。如果有多个线程在等待,则会选择一个。它会醒来,重新获取锁检查它的谓词。如果该谓词仍然是 false,它将 return 进入等待状态并且通知实际上丢失了。

这就是线程 运行 first 最后执行时发生的情况。当它到达 notify_one 时,将有两个线程等待 condition_variable。如果它通知线程 运行 third,它的谓词仍将是 return false。该线程将唤醒,它的谓词测试失败并且 return 等待。您的进程现在没有 运行 个线程并且被冻结。

解决方法是使用std::condition_variable::notify_all()。此函数唤醒 所有 等待线程,这些线程将一次一个地重新锁定 mutex 并检查它们自己的谓词。