我不确定我是否真的理解为什么std::condition_variable
需要额外的std::mutex
作为参数?它不应该被自己锁定吗?
#include <iostream>
#include <condition_variable>
#include <thread>
#include <chrono>
std::condition_variable cv;
std::mutex cv_m;
int i = 0;
bool done = false;
void waits()
{
std::unique_lock<std::mutex> lk(cv_m);
std::cout << "Waiting... \n";
cv.wait(lk, []{return i == 1;});
std::cout << "...finished waiting. i == 1\n";
done = true;
}
void signals()
{
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "Notifying falsely...\n";
cv.notify_one(); // waiting thread is notified with i == 0.
// cv.wait wakes up, checks i, and goes back to waiting
std::unique_lock<std::mutex> lk(cv_m);
i = 1;
while (!done)
{
std::cout << "Notifying true change...\n";
lk.unlock();
cv.notify_one(); // waiting thread is notified with i == 1, cv.wait returns
std::this_thread::sleep_for(std::chrono::seconds(1));
lk.lock();
}
}
int main()
{
std::thread t1(waits), t2(signals);
t1.join();
t2.join();
}
次要,在示例中,他们首先解锁互斥锁(signals
方法)。他们为什么要这样做? Shoulden首先锁定,然后在通知后解锁?
答案 0 :(得分:4)
互斥锁保护谓词,即你正在等待的东西。由于你要等待的东西必然是在线程之间共享的,所以它必须以某种方式受到保护。
在上面的示例中,i == 1
是谓词。互斥锁保护i
。
退一步思考为什么我们需要条件变量可能会有所帮助。一个线程检测到一些阻止它前进的状态,需要等待其他一些线程来改变该状态。这种状态检测必须在互斥锁下进行,因为必须共享状态(否则,另一个线程如何改变该状态?)。
但线程无法释放互斥锁然后等待。如果在互斥体发布之后但在线程设法等待之前状态发生了变化怎么办?所以你需要一个原子&#34;解锁并等待&#34;操作。这具体是条件变量提供的内容。
没有互斥锁,它们会解锁什么?
在释放锁之前或之后选择是否发出状态变量是一个复杂的选择,双方都有优势。
答案 1 :(得分:2)
使用多线程时要记住的一个好的经验法则是,当您提出问题时,结果可能是谎言。也就是说,答案可能已经改变,因为它是给你的。可靠地提出问题的唯一方法是使其有效地成为单线程。输入互斥锁。
条件变量等待触发器,以便它可以检查其状况。要检查其状况,需要提问。
如果您在等待之前没有锁定,那么您可能会问问题并获得条件,并且您被告知条件是错误的。当触发发生并且条件成立时,这变成了谎言。但你不知道这一点,因为没有互斥使这个有效的单线程。
相反,你在条件变量上等待一个永远不会触发的触发器,因为它已经完成了。这种僵局。