C++条件变量Wait及虚假唤醒
(1) wait(lock): 调用时即阻塞线程,并且调用lock.unlock()
(2) wait(lock, conditions): 调用时检查conditions,如果为false,则阻塞线程,并且调用lock.unlock(), 否则,继续执行、
(3) wait_for(lock, time_duration, conditions): 调用时,检查条件是否满足:(1) conditions返回true; (2) 时间超时,如果不满足(1)(2)中的一个条件,则阻塞线程,并调用lock.unlock(), 否则,到达一定等待时间或满足条件被唤醒 ,注意,等待超过时间段后自动唤醒,判断条件一般需要使用者自己在合适的时候判断,并通过notify_one()或notify_all()唤醒,所以,使用的时候注意判断返回值,即状态是否为std::cv_status::timeout
(4) wait_until(lock, time_point, conditions): 实际wait_for是通过wait_until实现,实际上也是一样的,到达指定时间点或满足条件conditions时被唤醒,注意,到达时间点是自动唤醒,判断条件一般需要使用者自己在合适的时候判断,并通过notify_one()或notify_all()唤醒,所以,使用的时候注意判断返回值,即状态是否为std::cv_status::timeout
什么是虚假唤醒?
举个例子,我们现在有一个生产者-消费者队列和三个线程。
1) 1号线程从队列中获取了一个元素,此时队列变为空。
2) 2号线程也想从队列中获取一个元素,但此时队列为空,2号线程便只能进入阻塞(cond.wait()),等待队列非空。
3) 这时,3号线程将一个元素入队,并调用cond.notify()唤醒条件变量。
4) 处于等待状态的2号线程接收到3号线程的唤醒信号,便准备解除阻塞状态,执行接下来的任务(获取队列中的元素)。
5) 然而可能出现这样的情况:当2号线程准备获得队列的锁,去获取队列中的元素时,此时1号线程刚好执行完之前的元素操作,返回再去请求队列中的元素,1号线程便获得队列的锁,检查到队列非空,就获取到了3号线程刚刚入队的元素,然后释放队列锁。
6) 等到2号线程获得队列锁,判断发现队列仍为空,1号线程“偷走了”这个元素,所以对于2号线程而言,这次唤醒就是“虚假”的,它需要再次等待队列非空。