CPP——并发编程(四)condition\_variable
到目前为止介绍的并发过程都是类似于去“争夺”控制,也就是只要有机会就去上,但是有时候我们会遇到那种等待的情况,达到某个条件了才能运行,这时候就需要用条件变量(condition_variable)。
关于条件变量的内容都在头文件中。它包含了std::condition_variable,std::condition_variable_any,还有枚举类型std::cv_status,函数std::notify_all_at_thread_exit()。这篇文章会介绍上面这些类以及函数的作用。
std::condition_variable
condition_variable的设定是这样的,它调用wait函数,使用unique_lock锁住当前线程,表明条件还未满足,这时候当前线程会被阻塞,直到另外一个线程在相同的condition_variable变量上调用了notification相关的函数,说明条件满足,则开始运行。
condition_variable只有默认构造函数,拷贝构造函数被删除。下面介绍它其他的成员函数。
wait
condition_variable有两个wait函数的重载。
1 | void wait (unique_lock<mutex>& lck);// unconditional |
wait接受一个参数,类型是unique_lock,调用wait函数后,当前线程被阻塞(当前线程已经获得了mutex的控制权,也就是mutex已经被锁住了),直到该condition_variable变量被notify之后,才能继续运行wait之后的内容。当线程被阻塞时,wait会调用unique_lock的unlock对mutex进行解锁,使得其他的线程可以占用mutex。当被notify之后,wait会调用lock来继续锁住mutex(如果此时被占用,当前线程继续被阻塞)。
对于predicate的wait,只有当predicate条件为false时候调用wait才会阻塞当前线程,并且只有当其他的线程通知predicate为true时才会解除阻塞,相当于又多了一个条件。predicate是一个callable的对象,不接受参数,并且能返回值可以转化成bool值。相当于:
1 | while (!pred()) wait(lck); |
wait_for
wait_for与wait一样,也有两个重载。
1 | template <class Rep, class Period>//unconditional |
wait_for和wait的区别在于它会接受一个时间段,在当前线程收到通知,或者在指定的时间内,它都会阻塞,当超时,或者收到了通知达到条件,wait_for返回,剩下的和wait一致。
wait_until
wait_until和wait_for类似,只不过时间范围被替换成了时间点。
1 | template <class Clock, class Duration>//unconditional |
notify_one
唤醒某个等待线程,如果没有线程在等待,那么这个函数什么也不做,如果有多个线程等待,唤醒哪个线程是随机的。
notify_all
唤醒所有的等待线程。
std::condition_variable_any
condition_variable_any和condition_variable的区别是wait接受的锁是任意的,而condition_variable只能接受unique_lock。
std::cv_status
这个枚举类型仅仅枚举了两个状态,标志着wait_for或者wait_until是否是因为超时解除阻塞的。
值 | 描述 |
---|---|
cv_status::no_timeout | wait_for 或者 wait_until 没有超时,即在规定的时间段内线程收到了通知 |
cv_status::timeout | wait_for 或者 wait_until 超时 |
std::notify_all_at_thread_exit
notify_all_at_thread_exit是一个函数,原型如下:
1 | void notify_all_at_thread_exit (condition_variable& cond, unique_lock<mutex> lck); |
当调用该函数的线程结束时,所有在cond上等待的线程都会收到通知。
总体来说,条件变量的相关内容还是很容易理解的。现在研究一下下面的代码:
1 |
|
10个线程被发出去后,都会因为ready是false而被wait阻塞,当ready为true时,唤醒了所有的线程,这时候线程之间的控制就又转成了对互斥量mutex的控制,因此输出顺序是不一致的。可能输出如下:
1 | 10 threads ready to race... |