简化的目标是强制在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。
什么是导致程序停止的调用路径?
我想念什么细微差别?
如果我在上述想法上有误,请纠正我。
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()
并检查其自己的谓词。