为什么重新使用条件变量的互斥会导致死锁?
当我寻求在C++ 11中实现std::condition_variable
的帮助时,我遇到了一个代码here。在上面的问题中,可以正确执行此类代码,但在函数void g()
中添加注释行会导致偶尔产生死锁。我想知道为什么和std::condition_variable::wait()
(cpp reference真的让我困惑)的内在机制。提前致谢。为什么重新使用条件变量的互斥会导致死锁?
#include <thread>
#include <mutex>
#include <condition_variable>
#include <iostream>
std::mutex mtx;
std::condition_variable cv;
void f() {
{
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock);
}
std::cout << "f()\n";
}
void g() {
// std::unique_lock<std::mutex> lock(mtx); adding this line will result in
// deadlock.
std::this_thread::sleep_for(std::chrono::seconds(1));
cv.notify_one();
}
int main() {
for (int i = 1; i <= 100; i++) {
std::cout << i << std::endl;
std::thread t1{ f };
std::thread t2{ g };
t2.join();
t1.join();
}
}
你应该与实际条件条件变量相关联,也占了虚假唤醒。在你的例子中,如果你先发送条件变量,然后通过wait()
进入条件变量睡眠,那么你的代码会死锁。
所以,你的代码应该理想地看起来像以下(其中,如果你的信号你wait()
睡觉前,变更后的条件将检测你不应该睡觉)
void f() {
{
std::unique_lock<std::mutex> lock(mtx);
while (some_boolean) {
cv.wait(lock);
}
}
std::cout << "f()\n";
}
void g() {
std::unique_lock<std::mutex> lock(mtx);
change_some_boolean();
cv.notify_one();
}
注意,它不当您致电notify_one()
g()
时,该锁是否处于锁定状态。但是,您应该确保在change_some_boolean()
之前持有锁。
答案是在例如在您提供的链接:Condition Variable on cppreference
// Manual unlocking is done before notifying, to avoid waking up
// the waiting thread only to block again (see notify_one for details)
lk.unlock();
cv.notify_one();
你的榜样然而,不前时,它应该使用notify_one()
解锁你的局部变量。你g()
功能应该是这样的:
void g() {
std::unique_lock<std::mutex> lock(mtx);
std::this_thread::sleep_for(std::chrono::seconds(1));
lock.unlock();
cv.notify_one();
}
我加入'''lock.unlock()'''后,我的代码仍然有一个死锁。 QAQ –
然后你可能完全错过了信号。你可能应该在每一行添加一些'cout'语句和'endl'。我敢打赌,“信号”线程在等待之前运行。您可以尝试在启动它们两者之间进行睡眠以尝试并保证执行顺序 –
@KevinAnderson - 不幸的是,插入I/O操作有时会改变计时,以至于最终隐藏问题。 –
创建,创建运行g()
不保证f()
将开始运行g()
做之前线程之前运行f()
线程。当g()
首先启动它抓住锁,睡一秒钟,然后通知的条件变量。由于没有人正在等待条件,所以通知不起作用。当g()
返回它释放锁。 然后f()
获取锁并拨打wait()
。没有人把它唤醒,并且f()
只是继续等待。这不是一个僵局;任何线程仍然可以调用notify()
并唤醒f()
。
注释行无法导致循环。也许你的意思是僵局? –
是的。这是一个僵局。对不起,我英文很差。 :) –