我有以下问题。我有一些多线程做一些工作,一个主线程在工作可用时唤醒它们。到目前为止,我已经设法使用条件变量和互斥锁编写了一些代码,并且大部分时间这都可以正常工作,但是通知线程会在调用notify_one()之后立即锁定互斥锁,从而阻止通知的线程和死锁
我写了最少的代码来说明这种情况。
#include <iostream>
#include <thread>
#include <condition_variable>
std::mutex lock;
std::condition_variable cv;
void foo() {
std::cout << "Thread: Entering doWork()" << std::endl;
std::unique_lock<std::mutex> l(lock);
std::cout << "Thread: Acquired lock, going to wait." << std::endl;
cv.wait(l , []{return true;});
std::cout << "Thread: Done waiting, exit." << std::endl;
}
int main(void) {
std::unique_lock<std::mutex> l(lock);
std::cout << "MAIN: Creating thread." << std::endl;
std::thread t(foo);
std::cout << "MAIN: Unlocking mutex." << std::endl;
l.unlock();
std::cout << "MAIN: Notifying thread." << std::endl;
cv.notify_one();
//std::this_thread::sleep_for(std::chrono::seconds(1));
l.lock();
std::cout << "MAIN: Acquired lock." << std::endl;
std::cout << "MAIN: Joining thread." << std::endl;
t.join();
return 0;
}
在理想情况下,输出应为
MAIN: Creating thread.
MAIN: Unlocking mutex.
Thread: Entering doWork()
Thread: Acquired lock, going to wait.
MAIN: Notifying thread.
Thread: Done waiting, exit.
MAIN: Acquired lock.
MAIN: Joining thread.
但往往是
MAIN: Creating thread.
MAIN: Unlocking mutex.
MAIN: Notifying thread.
MAIN: Acquired lock.
MAIN: Joining thread.
Thread: Entering doWork()
除了将睡眠添加到通知线程(我不想做)之外,有没有更好的方法来消除死锁的可能性?提前谢谢。
答案 0 :(得分:1)
它是“条件变量”而不是“条件变量”,它被调用的原因是你在某些条件下使用它来等待。
你没有这样做,你只是等待一个总是返回true的lambda,这就是问题的原因。那个和你的主线程一直持有锁的事实(为什么?!)
有时main
线程在foo
线程开始之前快速运行unlock,notify_one和锁定。这意味着foo
线程错过了通知,然后尝试获取锁,但不能,因为主线程有它。
条件变量与信号量不同,notify_one`调用不会设置稍后可以检测到的状态。如果在发生notify_one调用时条件变量没有等待,那么它会错过它,它会永远消失。如果你错过了通知然后再睡觉,你将永远不会醒来。
解决方案不添加任意睡眠,无法解决任何问题(永远!)
正确的解决方案是拥有一个经过测试的条件,并在您不更新任何共享数据时停止持有锁。在下面的示例中,正在测试的条件是“布尔值ready
是真的吗?”并且foo
线程将等待该条件为真。主线程设置变量,使条件成立,然后通知另一个线程它应该重新检查条件。
#include <iostream>
#include <thread>
#include <condition_variable>
std::mutex lock;
std::condition_variable cv;
bool ready = false;
void foo() {
std::cout << "Thread: Entering doWork()" << std::endl;
std::unique_lock<std::mutex> l(lock);
std::cout << "Thread: Acquired lock, going to wait." << std::endl;
cv.wait(l , []{return ready;});
std::cout << "Thread: Done waiting, exit." << std::endl;
}
int main(void) {
std::cout << "MAIN: Creating thread." << std::endl;
std::thread t(foo);
{
std::cout << "MAIN: Locking mutex." << std::endl;
std::unique_lock<std::mutex> l(lock);
ready = true;
}
std::cout << "MAIN: Notifying thread." << std::endl;
cv.notify_one();
std::cout << "MAIN: Joining thread." << std::endl;
t.join();
}