| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> C++知识库 -> C++并发编程(7):条件变量(conditional variable)、wait( )与notify_one( )、spurious wakeups(虚假唤醒) -> 正文阅读 |
|
[C++知识库]C++并发编程(7):条件变量(conditional variable)、wait( )与notify_one( )、spurious wakeups(虚假唤醒) |
并发操作的同步前面学习了如何保护线程间的共享数据。然而,有时候我们不仅需要保护共享数据,还需要令 上述线程间的同步操作很常见,C++标准库专门为之提供了处理工具:条件变量(conditional variable)和future 条件变量(conditional variable)设想你坐夜行列车外出。如果要保证在正确的站点下车,一种方法是彻夜不眠,留心列车停靠的站点,那样就不会错过。可是,到达时你可能会精神疲倦。或者,你可以查看时刻表,按预定到达时间提前设定闹钟,随后安心入睡。这种方法还算管用,一般来说,你不会误站。但若列车晚点,你反而会太早醒来;也可能不巧,闹钟的电池刚好耗尽,结果你睡过头而错过下车站点。最理想的方法是,安排人员或设备,无论列车在什么时刻抵达目的站点,都可以将你唤起,那么你大可“高枕无忧” 同样的,如果线程甲要等待线程乙完成任务,可以采取集中不同的方式 方式一:在共享数据内部维护一标志(受互斥保护),线程乙完成任务后,就设置标志成立 该方式存在双重浪费:线程甲须不断查验标志,浪费原本有用的处理时间;另外,一旦互斥被锁住,则其他任何线程无法再加锁。这两点都是线程甲的弊病:如果它正在运行,就会限制线程乙可用的算力;还有,线程甲每次查验标志,都要锁住互斥以施加保护,那么,若线程乙恰好同时完成任务,也意欲设置标志成立,则无法对互斥加锁。这就像是你整晚熬夜,不停地与列车司机攀谈,于是他不得不放慢车速,因为你老使他走神,结果列车晚点。类似地,线程甲白白耗费了计算资源,它们本来可用于系统中的其它线程,最终导致毫无必要的等待时间 方式二:让线程甲调用 std:this_thread:sleep for( )函数,在各次查验之间短期休眠
上面的代码在每轮循环中,先将互斥解锁,随之休眠,再重新加锁,从而其它线程有机会获取锁,得以设置标志成立 这确有改进,因为线程休眠,所以处理时间不再被浪费。然而,休眠期的长短却难以预知。休眠期太短,线程仍会频繁查验,虚耗处理时间;休眠期太长,则令线程过度休眠。如果线程乙完成了任务,线程甲却没有被及时唤醒,就会导致延迟。过度休眠很少直接影响普通程序的运作。但是,对于高速视频游戏,过度休眠可能会造成丢帧;对于实时应用,可能会使某些时间片计算超时 方式三:使用C++标准库的工具等待事件发生 以上述甲、乙两线程的二级流水线模式为例,若数据要先进行前期处理,才可以开始正式操作,那么线程甲则需等待线程乙完成并且触发事件,其中最基本的方式是条件变量。按照“条件变量”的概念,若条件变量与某一事件或某一条件关联,一个或多个线程就能以其为依托,等待条件成立。当某线程判定条件成立时,就通过该条件变量,知会所有等待的线程,唤醒它们继续处理 condition_variable头文件<condition_variable>
相同点:两者都能与std::mutex一起使用 不同点:前者仅限于与 std::mutex 一起工作,而后者可以和任何满足最低标准的互斥量一起工作,从而加上了_any的后缀,condition_variable_any会产生额外的开销 一般只推荐使用condition_variable,除非对灵活性有硬性要求,才会考虑condition_variable_any 条件变量的构造函数:
示例代码:
打印输出:
wait( )与notify_one( )wait( )函数
std::condition_variable 提供了两种 wait() 函数。当前线程调用 wait() 后将被阻塞(此时当前线程应该获得了锁(mutex),不妨设获得锁 lck),直到另外某个线程调用 notify_* 唤醒了当前线程 在线程被阻塞时,该函数会自动调用
wait( )函数在第二种情况下(即设置了 Predicate),只有当 pred 条件为 false 时调用 wait( ) 才会阻塞当前线程,并且 示例代码:
打印输出:
spurious wakeups(虚假唤醒)需要注意的一点是, wait有时会在没有任何线程调用notify的情况下返回,这种情况就是有名的spurious wakeup 这种情况会出现在第二种wait函数中,即:
spurious wake产生的原因就是当前置判断条件pred为true时,即使没有线程notify,只要锁已经被释放,wait函数就会立刻返回,继续执行下面的代码 如将上面的示例程序稍加修改:
主函数休眠5秒后释放锁,worker线程依旧可以继续执行 所以在实际使用中我们要添加一些判断条件尽量避免虚假唤醒的出现 参考博客 |
|
C++知识库 最新文章 |
【C++】友元、嵌套类、异常、RTTI、类型转换 |
通讯录的思路与实现(C语言) |
C++PrimerPlus 第七章 函数-C++的编程模块( |
Problem C: 算法9-9~9-12:平衡二叉树的基本 |
MSVC C++ UTF-8编程 |
C++进阶 多态原理 |
简单string类c++实现 |
我的年度总结 |
【C语言】以深厚地基筑伟岸高楼-基础篇(六 |
c语言常见错误合集 |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/23 18:35:03- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |