何时需要线程同步
线程完成前,需要等待另一个线程执行
线程需要等待特定事件发生
线程等待某个条件变为true
线程同步的方式
持续检查共享标记
voidwait_for_flag(){std::unique_locklock(m);while(!flag){lock.unlock();lock.lock();}do_something();}
2.等待线程在检查间隙
voidwait_for_flag(){std::unique_locklock(m);while(!flag){lock.unlock();std::this_thread::sleep_for(std::chrono::milliseconds());//休眠lock.lock();}//goingonnext}
3.条件变量(conditionvariable)
1条件变量使用方式
目前有以下两种方式,两者都需要与一个互斥量才能工作。
std::condition_variable:仅限于和std::mutex一起工作
std::condition_variable_any:可以和任何满足最低标准的互斥量一起工作,更加通用,但是体积、性能、系统资源会产生额外的开销
std::mutexlock;std::queuedata_setdata_queue;std::condition_variabledata_cond;voiddata_preparation_thread(){while(more_data_to_prep()){data_setconstdata=prep_data();std::lock_guardl(lock);data_queue.push(data);/**1.notify_one()触发一个正在执行wait()的线程,去检查条件和wait()函数的返回状态2.另一种可能是,很多线程等待同一事件,对于通知他们都需要做出回应,需要使用notify_all()**/data_cond.notify_one();}}voiddata_processing_thread(){while(true){std::unique_lockl(lock);//后续需要unlock,因此不能用lock_guard/*1.在条件满足的情况下,从wait()返回并继续持有锁。当条件不满足时,线程将对互斥量解锁,并且重新开始等待2.当等待线程重新获取互斥量并检查条件时,如果它并非直接响应另一个线程的通知,这就是所谓的“伪唤醒”(spuriouswakeup)。*/data_cond.wait(l,[]{return!data_queue.empty();});/**另一种形式:if(data.queue.empty()){data_cond.wait(l);}**/data_setdata=data_queue.front();data_queue.pop;l.unlock();process(data);if(is_last_data(data)){break;}}}
3.2使用期待处理一次性事件
当等待线程只等待一次,当条件为true时,它就不会再等待条件变量了,那么这种情况下使用条件变量会存在一定的浪费。
C++将这种一次性事件称为“期望”(future)。当一个线程需要等待一个特定的一次性事件时,future有以下集中应用方式:
这个线程可以周期性(较短的周期)的等待或检查,事件是否触发(检查信息板);
在检查期间也可以执行其他任务;
在等待任务期间它可以先执行另外一些任务,直到对应的任务触发,而后等待期望的状态会变为“就绪”(ready)
C++有两种期望类型:std::future和std::shared_future。std::future的实例只能与一个指定事件相关联,而std::shared_future的实例就能关联多个事件。后者的实现中,所有实例会在同时变为就绪状态,并且他们可以访问与事件相关的任何数据。
3.2.1std::future
比如需要一个长时间的运算,但是现在并不需要