C++ 命令多线程如何重新获取std::unique_lock<;std::mutex>;在std::condition_变量::notify_all之后
下面是一些将从多个线程调用的代码。对于每个线程,C++ 命令多线程如何重新获取std::unique_lock<;std::mutex>;在std::condition_变量::notify_all之后,c++,multithreading,c++11,C++,Multithreading,C++11,下面是一些将从多个线程调用的代码。对于每个线程,std::condition\u variable::wait的谓词将略有不同,但不会改变问题 std::unique_lock<std::mutex> lock{connection_mutex}; cv.wait(lock, [conn = shared_from_this()] { return conn->connection_is_made(); } ); //do s
std::condition\u variable::wait
的谓词将略有不同,但不会改变问题
std::unique_lock<std::mutex> lock{connection_mutex};
cv.wait(lock,
[conn = shared_from_this()]
{
return conn->connection_is_made();
}
);
//do some stuff
lock.unlock();
std::unique_lock lock{connection_mutex};
cv.等待(锁定,
[conn=shared_from_this()]
{
返回连接->连接已建立();
}
);
//做点什么
lock.unlock();
我在一家报纸上读到这篇文章
当通知条件变量、超时过期或发生虚假唤醒时,线程被唤醒,互斥锁被原子地重新获取。然后,如果唤醒是虚假的,线程应该检查条件并继续等待
由此我了解到lock
将被锁定,而谓词lambda将被检查,如果它返回true
则lock
将保持锁定状态,我可以在这个互斥锁的保护下继续做一些事情。当std::condition\u变量
由notify\u one()
成员函数通知时,它就非常有意义了
但是当notify\u all()
通知std::condition\u变量时会发生什么情况呢?我在文档中找不到,如果所有线程都被唤醒,然后“排队等待”锁定互斥锁,然后才检查谓词返回的内容,或者它们执行其他操作
编辑:看到下面的评论后,我开始思考std::condition_variable::wait
期望将std::unique_lock
作为其第一个参数,并且在通知后唤醒std::condition_variable::wait
时,将重新获取std::unique_lock
。现在,如果多个线程正在等待同一个通知,那么在特定的std::condition\u变量上调用notify\u all()
时,只有一个线程能够锁定互斥锁,而所有其他线程将返回睡眠状态。因此,如果notify\u all()
成员函数与notify\u one()
具有相同的效果,我看不出有任何意义,但效率较低。我的意思是,如果必须为
如果线程要通过std::condition_variable::wait
,那么所有等待的线程不可能同时执行该操作。我做了一些测试,以查看调用notify_all()后是否所有线程都被唤醒,即使那些没有被安排作为第一个线程唤醒的线程也是如此
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <chrono>
std::mutex M;
std::condition_variable CV;
int var = 0;
int main()
{
std::thread t1{
[]{
std::unique_lock<std::mutex> lck{M};
while(var != 1){
CV.wait(lck);
std::cout << "t1 woken up and (i != 1) = "
<< (var != 1) << std::endl;
}
}
};
std::thread t2{
[]{
std::unique_lock<std::mutex> lck{M};
while(var != 2){
CV.wait(lck);
std::cout << "t2 woken up and (i != 2) = "
<< (var != 2) << std::endl;
}
}
};
std::thread t3{
[]{
std::unique_lock<std::mutex> lck{M};
while(var != 3){
CV.wait(lck);
std::cout << "t3 woken up and (i != 3) = "
<< (var != 3) << std::endl;
}
}
};
std::thread t4{
[]{
std::unique_lock<std::mutex> lck{M};
while(var != 4){
CV.wait(lck);
std::cout << "t4 woken up and (i != 4) = "
<< (var != 4) << std::endl;
}
}
};
for(int i = 0; i < 6; ++i){
std::unique_lock<std::mutex> lck{M};
var = i;
CV.notify_all();
lck.unlock();
std::this_thread::sleep_for(std::chrono::seconds{1});
std::cout << "\n\n";
}
t1.join();
t2.join();
t3.join();
t4.join();
}
因此,我很高兴看到,无论首先唤醒哪个线程,只要调用notify\u all()
,所有被通知的线程都会以某种随机顺序被唤醒。看起来像是当notify_all()
(假设线程A)唤醒的其中一个线程在完成某项工作后正在解锁互斥锁时,下一个线程被与线程A相同的notify_all()
调用唤醒,该线程A正在自动锁定刚刚解锁的互斥锁。直到被notify_all()
唤醒的所有线程都锁定/解锁了用于保护共享数据的同一mutex
。如果您询问通知顺序,这可能会有所帮助:感谢您的回答,但在这种情况下,我不担心永远不会收到notify_all()信号。我只想知道当多个线程等待同一个条件变量时,它们是如何工作的,然后通过notify_all()接收通知,然后尝试获取相同的互斥量。这取决于OS调度程序。你不知道哪个线程先得到互斥锁。他们在排队等待?1个线程得到它,完成它的工作,当互斥被释放时,另一个线程立即重新获取互斥并检查其谓词,依此类推,直到所有等待的线程重新获取互斥?根据cppreference,所有线程都被唤醒。但是,如果他们只是为了一个互斥而“排队”,那么除了一个之外,所有人都会回到睡眠状态。那么在这种情况下,为什么要调用notifyall而不是notifyone呢?如果所有等待的线程都能同时取得进展,那么通知所有线程是很好的。
t3 woken up and (i != 3) = 1 //spurious wakeup
t3 woken up and (i != 3) = 1
t4 woken up and (i != 4) = 1
t2 woken up and (i != 2) = 1
t1 woken up and (i != 1) = 0
t3 woken up and (i != 3) = 1
t4 woken up and (i != 4) = 1
t2 woken up and (i != 2) = 0
t3 woken up and (i != 3) = 0
t4 woken up and (i != 4) = 1
t4 woken up and (i != 4) = 0