无限等待条件变量

问题描述 投票:0回答:1

简化的目标是强制在3个不同的线程中一个一个地调用3个成员函数(线程A调用F :: first,线程B F :: second,线程C F :: 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. 我想念什么细微差别?

如果我在上述想法上有误,请纠正我。

c++ multithreading deadlock condition-variable
1个回答
1
投票

std::condition_variable::notify_one()将唤醒等待std::condition_variable::notify_one()的线程中的[[one”。如果有多个线程正在等待,则将选择一个线程。它将唤醒,重新获取其谓词的锁定检查。如果该谓词仍然为condition_variable,它将返回其等待状态,并且通知实际上丢失了。

这是最后运行的false线程在这里发生的情况。到达first时,将有两个线程在等待notify_one。如果它通知正在运行condition_variable的线程,则该谓词仍将返回third。该线程将唤醒,无法通过谓词测试,然后返回等待状态。您的进程现在没有正在运行的线程,并且已冻结。

解决方案是使用false。此函数唤醒

all

个等待中的线程,这些线程将一次重新锁定std::condition_variable::notify_all()并检查其自己的谓词。
© www.soinside.com 2019 - 2024. All rights reserved.