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