在std :: condition_variable :: notify_all

时间:2017-04-22 17:45:28

标签: c++ multithreading c++11

以下是将从多个线程调用的一段代码。 std::condition_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();

我在cppreference

上看到了这个
  

当通知条件变量,超时到期或发生虚假唤醒时,线程被唤醒,并且原子重新获取互斥锁。然后线程应该检查条件并在唤醒是假的时候继续等待。

据此我了解lock将被锁定,而不会检查谓词lambda,如果它返回truelock将保持锁定状态,我可以继续执行某些操作在这个互斥锁的保护下的东西。当std::condition_variable成员函数通知notify_one()时,它非常有意义。

但是std::condition_variable通知notify_all()时会发生什么?我无法在文档中找到所有线程被唤醒而不是“等待一行”来锁定互斥锁,然后检查谓词返回的内容或者他们可能做其他事情。

编辑:在看到下面的评论之后我开始思考了一下。 std::condition_variable::wait期望std::unique_lock作为其第一个参数,并且在通知后唤醒std::condition_variable::wait时,将重新获取std::unique_lock。现在,如果多个线程正在等待相同的通知,那么最终在特定notify_all() 上调用std::condition_variable时,只有1个线程可以锁定互斥锁,所有其他线程将重新进入休眠状态。所以,如果它与notify_all()具有相同的效果,除非效率较低,我认为没有任何关于notify_one()成员函数的意义。我的意思是如果必须重新获取互斥锁 一个要经过std::condition_variable::wait的线程,那么所有等待的线程都无法同时执行它。

1 个答案:

答案 0 :(得分:0)

我做了一些测试,看看在调用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();
}  

以下是结果

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

所以我很高兴看到无论第一次唤醒什么线程,所有通知的线程都会以一个随机的顺序被唤醒,只需一次调用notify_all()。看起来当notify_all()之一被唤醒的线程(比如线程A)在执行某个工作后解锁mutex时,下一个被同一个notify_all()调用唤醒的线程因为线程A自动锁定线程A刚刚解锁的mutex。这一直持续到notify_all()唤醒的所有线程锁定/解锁用于保护共享数据的相同mutex