有时候 thread需要等待某种外部事件,比如另一个 thread完成了任务或是已经过去了段时间。最简单的“事件”就是时间流逝。请考虑如下的代码
//获取时间差using namespace std:chrono; //见35.2节auto t0 = high_resolution_clock::now();this_thread::sleep_for(milliseconds{20});auto t1= high_resolution_clock:now();cout << duration_cast<nanoseconds>(t1-t0).count()<<"nanoseconds passed\n";
注意,我甚至没有启动一个 thread, this thread默认指向唯一的线程(见42.2.6节)。
我使用 duration_cast将时钟单位调整为期望的纳秒。要想尝试更多关于时间的操作请先阅读5.41节和35.2节。C++的时间功能位于<chrono>头文件中。
通过外部事件实现线程间通信的基本方法是使用 condition_variable,它定义在<condition_variable>中(见42.3.4节)。 condition_variable提供了一种机制,允许一个thread等待另一个 thread。特别是,它允许一个thread等待某个条件( condition,通常称为一个事件, event)发生,这种条件通常是其他 thread完成工作产生的结果。
考虑两个 thread通过 queue传递消息的经典例子。为简单起见,我声明 queue对象,以及生产者、消费者共享 queue同时避免竞争条件的机制如下:
class Message{ //通信的对象//...}queue<Message> mqueue; //消息的队列condition_variable mcond; //通信用的条件变量mutex mmutex; //锁机制
其中的类型queue,condition_variable和mutex由标准库提供。consumer()读取并处理Message:
void consumer(){while(true){unique_lock<mutex> lck{mmutex}; //获取mmutexwhile(mcord.wait(lck)){//do nothing //释放lck并等待} //被唤醒后重新获取lckauto m=mqueue.front(); //获取消息mqueue.pop();lck.unlock();//处理m}}
此例中,我通过一个 mutex上的 unique_lock显式保护对 queue和 condition_variable的操作。线程在 condition_variable上等待时,会释放已持有的锁,直至被唤醒后(此时队列非空)重新获取锁。
对应的 producer可以这样编写:
void producer(){while(true){Message m;//填入消息unique_lock<mutex> lck{mmutex}; //保护队列上的操作mqueue.push(m);mcond.notify_one(); //通知} //释放锁(在作用域结束)}
使用 condition_variable可以帮助我们完成很多既优雅又有效的数据共享,但是绝非一直如此(见42.3.4节)。
