一、主要涉及
- 等待事件
- 带有期望的等待一次性事件
- 在限定时间内等待
- 使用同步操作简化代码
在上一章中,我们看到各种在线程间保护共享数据的方法。当你不仅想要保护数据,还想对单独的线程进行同步。例如,在第一个线程完成前,可能需要等待另一个线程执行完成。 C++ 标准库提供了一些工具可用于同步操作,形式上表现为条件变量(condition variables)和期望(futures)。
二、等待一个条件或者事件
1、选择是在等待线程在检查间隙,使用 std::this_thread::sleep_for() 进行周期性的间歇;
2、(也是优先的选择)是,使用C++标准库提供的工具去等待事件的发生。通过另一线程触发等待事件的机制是最基本的唤醒方式(例如:流水线上存在额外的任务时),这种机制就称为“条件变量”。从概念上来说,一个条件变量会与多个事件或其他条件相关,并且一个或多个线程会等待条件的达成。
C++标准库对条件变量有两套实现: std::condition_variable 和 std::condition_variable_any
展示了一种使用条件变量做唤醒的方式:
std::mutex mut; std::queue<data_chunk> data_queue; // 1 std::condition_variable data_cond;
//准备数据线程 void data_preparation_thread() { while(more_data_to_prepare()) { data_chunk const data=prepare_data(); std::lock_guard<std::mutex> lk(mut);//队列上锁 data_queue.push(data); // 2 将数据压入队列 data_cond.notify_one(); // 3std::condition_variable 的notify_one()成员函数,对等待的线程(如果有等待
//线程)进行通知
}
}
//处理数据线程
void data_processing_thread()
{ while(true) { std::<std::mutex> lk(mut); // data_cond.wait( lk,[]{return !data_queue.empty();}//检测队列是否有数据写入了
); // 5 data_chunk data=data_queue.front();//对数据处理 data_queue.pop(); lk.unlock(); // 6 process(data); if(is_last_chunk(data)) break; } }
wait()会去检查这些条件(通过调用所提供的lambda函数),当条件满足(lambda函数返回true)
时返回。如果条件不满足(lambda函数返回false),wait()函数将解锁互斥量,并且将这个线程
(上段提到的处理数据的线程)置于阻塞或等待状态。
条件变量时,处理数据的线程从睡眠状态中苏醒,重新获取互斥锁,并且对条件再次检查,在条件满足的情况下,从wait()返回并继续持有锁。当条件不满足时,线程将对互斥量解锁,并且重新开始等待。这就是为什么用 std::unique_lock (4)而不使用 std::lock_guard ——等待中的线程必须在等待期间解锁互斥量,并在这这之后对互斥量再次上锁。
三、期望
四、信号量