本文主要说明条件变量的一些知识。
I、等待一个时间或其他条件
使用C++标准库提供的工具去等待事件的发生。通过另一个线程触发等待事件的机制是最基本的唤醒方式,这种机制就是条件变量。
1.1 等待条件达成
C++标准库对条件变量有两套实现:std::condition_variable
和std::condition_variable_any
。两者都是需要与一个互斥量才能工作(互斥量是为了同步)。
下面代码说明了当有数据需要处理时,唤醒休眠中的线程对其进行处理的方式:
#include<iostream>
#include<mutex>
#include<condition_variable>
#include<queue>
#include<thread>
using namespace std;
std::mutex mut;
std::queue<data_chunk> data_queue; //数据队列
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); //
data_cond.notify_one(); //通知等待线程
}
}
//data处理线程
void data_processing_thread() {
while (true) {
std::unique_lock<std::mutex> lk(mut); //对互斥量上锁,保护条件变量
/* wait传入一个互斥量和检查条件
* wait检查这些条件,如果条件不满足(为false)
* wait将解锁互斥量,并将这个线程置于阻塞或等待状态。
* 当准备数据的线程调用notify通知条件变量时,处理数据的线程从休眠中苏醒,重新获取互斥锁。
* 并对条件再次检查,在条件满足的情况下,从wait返回并继续持有锁。当条件不满足时,线程对互斥量解锁,并重新开始等待。
* 如果互斥量在线程休眠期间保持锁住状态,准备数据的线程将无法锁住互斥量,也就无法添加新的数据到队列中。
*/
data_cond.wait(lk, []{return !data_queue.empty();}); //等待条件
data_chunk data = data.front();
data_queue.pop();
lk.unlock(); //
process(data);
if (is_last_chunk(data))
break;
}
}
II、使用期望等待一次性事件
C++标准库模型将一次性事件成为期望(future)。当一个线程需要等待一个特定的一次性事件时,在某种程度上来说它就需要知道这个事件在未来的表现形式。之后,这个下线程会周期性的等待或检查事件是否触发;在检查期间也会执行其他任务。另外,在等待任务期间它可以先执行另外一些任务,知道对应的任务触发,而后等待期望的状态变为就绪(ready)。当时间发生时,这个期望就不会被重置。
C++标准库中,使用两种模板实现了两种期望:唯一期望std::future<>
和共享期望std::shared_future<>
。
期望对象本身并不提供同步访问。当多个线程需要访问同一个独立期望对象时,它们必须使用互斥量或类似的同步机制对访问进行保护。
2.1 带返回值的后台任务
假设有一个需要长时间计算的任务,但是现在并不迫切需要这个计算结果。可以启动一个新线程来执行这个计算,但是这就意味着必须关注如何传回计算的结果,因为std::thread
并不提供直接接收返回值的机制。这里需要std::async
函数模板。当任务的结果并不着急要时,可以使用std::async
启动一个异步任务。与std::thread
对象等待方式不同,std::async
会返回一个std::future
对象,这个对象就是最终计算出来的结果。当需要这个值时,只需要调用这个对象的get()
成员函数:
#include<future>
#include<iostream>
using namespace std;
int find_the_answer_to_ltuar();
void do_other_stuff();
int main() {
//也可以想async中传递参数
std::future<int> the_answer = std::async(find_the_answer_to_ltuae);
do_other_stuff();
cout << the_answer.get() << endl;
return 0;
}
【参考】
[1] 《C++ Concurrency In Action》
网友评论