6 - 条件变量
条件变量允许一个线程堵塞,直到另一个线程设置某个条件或系统时间到达某个指定的时间。条件变量允许显式的线程间通信,如果熟悉Win32API的多线程编程,可将条件变量和Windows种的事件对象进行比较。
条件变量
简介
有两类条件变量,它们都定义在<condition_variable>
中。
std::condition_variable
:只能等待unique_lock<mutex>
上的条件变量,在特定平台上的效率高。std::condition_variable_any
:可等待任何对象的条件变量,包括自定义锁类型。
方法
条件变量condition_variable
类支持如下方法:
notify_one()
:唤醒等待该条件变量的线程之一。notify_all()
:唤醒等待该条件变量的所有线程。wait(unique_lock<mutex>& lk)
:使用lk
锁住该线程并阻塞,直到另一个线程唤醒它。wait_for(unique_lock<mutex>& lk, const chrono::duration<Rep, Period>& rel_time)
:和wait()
类似,但超过给定的相对时间后也能解除该线程阻塞。wait_until(unique_lock<mutex>& lk, const chrono::duration<Clock, Duration>& abs_time)
:和wait()
类似,但超过给定的绝对时间后解除该线程阻塞。
condition_variable_any
类也支持上述方法,但它可接收任意实现了lock()
和unlock()
方法的锁。
需要注意的是,即使没有其他线程调用任何通知方法,线程也有可能会醒过来。因此当线程等待一个条件变量并醒过来后,需要检查它是否通过通知方法醒过来。
使用
条件变量可用于处理队列项的后台线程:在队列中插入要处理的项,后台线程等待队列中出现项,有的话让线程苏醒处理,没有就继续休眠。
假设有如下队列:
queue<string> m_queue;
需要确保在任何时候只有一个线程修改这个队列,可用互斥体实现这一点:
mutex m_mutex;
为了能在添加一项时通知后台线程,还需要一个条件变量:
condition_variable m_cond_var;
如果要往队列里添加一项,需要先获得互斥体上的锁,然后往队列中添加项,最后通知后台线程:
unique_lock lock(m_mutex);
m_queue.push("Thing");
m_cond_var.notify_all();
后台线程在一个无尽循环中等待通知,这里使用带有谓词的wait()
,队列不空时才苏醒:
unique_lock lock(m_mutex);
while (true)
{
m_cond_var.wait(lock, [this] {return !m_queue.empty(); });
// 处理项......
string item = m_queue.front();
}
需要确保代码中不会出现死锁。
参考资料
- C++20高级编程