等待一个事件
在多线程开发中,当一个线程的运行条件是另外一个线程的运算结果的时间,等待线程通常有几种处理方法1. 循环查询,知道满足条件为止 2. 休眠一个固定的时间,然后查询条件,当不满足的时候再继续休眠相同时间,知道下一次查询满足条件 3. 使用条件变量
下面分别对后两种方法进行阐述
- 休眠固定时间
使用std::sleep_for()
进行周期性的间歇bool flag mutex m; void wait_for(){ unique_lock<mutex>uloc(m); while(!flag){ uloc.unlock(); this_thread::sleep_for(chrono::nilliseconds(100)); uloc.lock(); } }
这种方法的优缺点也很直觉,优点:代码简单易懂,缺点:睡眠的时间难以掌握
- 使用条件变量
C++标准库对于条件变量有两套实现:std::condition_variable
和std::condition_variable_any
两个实现都包含在头文件<condition_variable>
中,两者都需要一个互斥量才能工作,前者仅能和mutex一起工作,而后者可以和各种互斥量进行工作,因为后者更加通用,所以导致从体积,性能,以及系统资源等方面有着更大的消耗,在一般的开发过程中,前者为首选类型,当系统对于灵活性有着硬性要求的时候才考虑后者
下面的代码展示了condition_variable的基本使用:mutex m; queue<int>que; condition_variable que_cond; void data_prepare_thread(){ while(more_prepare_data()){ auto data = prepare_data(); lock_guard<mutex>loc(m); que.push(data); que_cond.notify_once(); } } void data_process_thread(){ while(true){ unique_lock<mutex>lk(m); que_cond.wait(lk,[&que](){return !que.empty();}); auto data = que.front(); que.pop(); lk.unlock(); process(data); if(is_lask_chunk(data)) break; } }
对于生产者,首先对共享资源上锁,然后将准备的数据添加进入共享资源,最后调用condition_variable中的notify_one()成员函数对等待的线程进行通知
对于消费者,首先对共享资源上锁,调用condition_variable中的wait方法,wait首先回去检查第二参数是否满足条件,如果满足则继续持有锁,进行下面的操作,如果不满足条件则释放锁,然后置于阻塞或者等待状态,知道进程被唤醒。
注意,不一定是另一个线程调用notify的时候wait状态才会被唤醒,某些条件下wait状态的线程也会被唤醒,这就是所谓的伪唤醒,因此判断条件的合适性显得尤为重要。