TL;DR 如果你能一眼看出 https://gist.github.com/chenshuo/6430925 中的那 8 个 Waiter classes 哪些是对的哪些是错的,本文就不必看了。

前几天,我发了一条微博 http://weibo.com/1701018393/A7FrW7ZVd ,质疑某本书对 Pthreads 条件变量的封装是错的,因为它没有把 mutex 的 lock()/unlock() 函数暴露出来,导致无法实用。后来大家讨论的分歧是这个 cond class 是不是通用的条件变量封装,还是只是一个特殊的“事件等待器”。作为事件等待器,其实现也是错的,因为存在丢失事件的可能,可以算是初学者使用条件变量的典型错误。

本文的代码位于 recipes/thread/test/Waiter_test.cc,这里提到的某书的版本相当于 Waiter1 class。

我在拙作《Linux 多线程服务端编程:使用 muduo C++ 网络库》第 2.2 节总结了条件变量的使用要点:

条件变量只有一种正确使用的方式,几乎不可能用错。对于 wait 端:
1. 必须与 mutex 一起使用,该布尔表达式的读写需受此 mutex 保护。
2. 在 mutex 已上锁的时候才能调用 wait()。
3. 把判断布尔条件和 wait() 放到 while 循环中。

对于 signal/broadcast 端:
1. 不一定要在 mutex 已上锁的情况下调用 signal (理论上)。
2. 在 signal 之前一般要修改布尔表达式。
3. 修改布尔表达式通常要用 mutex 保护(至少用作 full memory barrier)。
4. 注意区分 signal 与 broadcast:“broadcast 通常用于表明状态变化,signal 通常用于表示资源可用。(broadcast should generally be used to indicate state change rather than resource availability。)”

如果用条件变量来实现一个“事件等待器/Waiter”,正确的做法是怎样的?我的最终答案见 WaiterInMuduo class。“事件等待器”的一种用途是程序启动时等待初始化完成,也可以直接用 muduo::CountDownLatch 到达相同的目的,将初值设为 1 即可。

以下根据微博上的讨论过程给出几个正确或错误的版本,博大家一笑。只要记住 Pthread 的条件变量是边沿触发(edge trigger),即 signal()/broadcast() 只会唤醒已经等在 wait() 上的线程(s),我们在编码时必须要考虑 signal() 早于 wait() 的可能,那么就很容易判断以下各个版本的正误了。代码见 recipes/thread/test/Waiter_test.cc。

版本一:错误。某书上的原始版,有丢失事件的可能。

版本二:错误。lock() 之后再 signal(),同样有丢失事件的可能。

版本三:错误。引入了 bool signaled_; 条件,但没有正确处理 spurious wakeup。

版本四五六:正确。仅限 single waiter 使用。

版本七:最佳。可供 multiple waiters 使用。

版本八:错误。存在 data race,且有丢失事件的可能。理由见 http://stackoverflow.com/questions/4544234/calling-pthread-cond-signal-without-locking-mutex

总结:使用条件变量,调用 signal() 的时候无法知道是否已经有线程等待在 wait() 上。因此一般总是要先修改“条件”,使其为 true,再调用 signal();这样 wait 线程先检查“条件”,只有当条件不成立时才去 wait(),避免了丢事件的可能。换言之,通过使用“条件”,将边沿触发(edge trigger)改为电平触发(level trigger)。这里“修改条件”和“检查条件”都必须在 mutex 保护下进行,而且这个 mutex 必须用于配合 wait()。

思考题:如果用两个 mutex,一个用于保护“条件”,另一个专门用于和 cond 配合 wait(),会出现什么情况?

最后注明一点,http://stackoverflow.com/questions/6419117/signal-and-unlock-order 这篇帖子里对 spurious wakeup 的解释是错的,spurious wakeup 指的是一次 signal() 调用唤醒两个或以上 wait()ing 的线程,或者没有调用 signal() 却有线程从 wait() 返回。manpage 里对 Pthreads 系列函数的介绍非常到位,值得细读。

转载于:https://www.cnblogs.com/Solstice/p/3309089.html

用条件变量实现事件等待器的正确与错误做法相关推荐

  1. Python——信号量、条件变量、事件

    1.信号量(Semaphore) 信号量通常用于保护数量有限的资源,例如数据库服务器.在资源数量固定的任何情况下,都应该使用有界信号量.在生成任何工作线程前,应该在主线程中初始化信号量. 信号量提供a ...

  2. 计算机检查例外 错误类型,事件查看器出现致命硬件错误,电脑频繁死机

    补充一些截图和报错信息. 出现致命硬件错误. 由以下组件报告: 处理器核心 错误源: 计算机检查例外 错误类型: 内部计时器错误 处理器 APIC ID: 0 出现致命硬件错误. - - 18 0 2 ...

  3. 【Python】条件变量、信号变量、事件

    条件变量.信号变量.事件 信号量:信号量是用来解决线程同步和互斥的通用工具, 和互斥量类似, 信号量也可以用作于资源互斥访问, 但信号量没有所有者的概念,在应用上比互斥量更广泛,信号量比较简单, 不能 ...

  4. 【C++】多线程与条件变量【三】

    文章目录 1 条件变量是什么? 实例1: 2 条件变量本质? 3 引入条件变量的原因? 实例2: 实例3: 实例4: 4 如何使用条件变量? 4.1 std::condition_variable 实 ...

  5. 条件变量为什么要和互斥锁一起使用

    mutex体现的是一种竞争,我离开了,通知你进来. cond体现的是一种协作,我准备好了,通知你开始吧. 互斥锁一个明显的缺点是它只有两种状态:锁定和非锁定.而条件变量通过允许线程阻塞和等待另一个线程 ...

  6. 进程通信学习笔记(互斥锁和条件变量)

    1.互斥锁:上锁和解锁 Posix互斥锁作为数据类型pthread_mutex_t的变量声明.如果互斥锁变量是静态分配的,那么可以把它初始化成常值PTHREAD_MUTEX_INITIALIZER.如 ...

  7. php 语法 条件变量,C ++核心准则:注意条件变量的陷阱

    今天,我写了一篇关于条件变量的恐怖文章.您应该意识到条件变量的这一问题.C ++核心准则CP 42仅声明:"不要无条件等待". 等待!条件变量支持一个非常简单的概念.一个线程准备一 ...

  8. Linux线程同步之条件变量

    与互斥锁不同,条件变量是用来等待而不是用来上锁的.条件变量用来自动阻塞一个线程,直到某特殊情况发生为止.通常条件变量和互斥锁同时使用. 条件变量使我们可以睡眠等待某种条件出现.条件变量是利用线程间共享 ...

  9. 3线程同步:条件变量

    1条件变量 条件变量给多个线程提供了一个汇合的场所. 依赖的头文件 #include<pthread.h> 函数声明 定义分配条件变量 pthread_cond_t cond =PTHRE ...

最新文章

  1. multipart request_Request和Response
  2. 2021第三封拒信来自哈佛大学计算机科学与技术专业~
  3. 线段树杭电1754 I hate it
  4. rgb sw 线主板接口在哪_十代至尊i910980XE直接上:技嘉X299X AORUS MASTER主板评测
  5. 使用Oracle验证外部数据
  6. 云小课 | 网站接入WAF失败怎么办?看这里就够了
  7. Python稳基修炼的经典案例11(计算机二级、初学者必会输入输出训练)
  8. Windows文件系统过滤驱动开发教程(4)
  9. 彻底搞懂Html5本地存储技术(一)
  10. 安卓和ios的ui设计区别_简析Android系统与ios系统界面设计区别
  11. 泽林主办前沿IT技术分享峰会隆重召开,深度探讨人工智能、大数据与物联网 的未来发展趋势
  12. Windows+Caffe+VS2013+python接口配置过程
  13. 201771010112罗松《面向对象程序设计(java)》第八周学习总结
  14. 使用jemdoc制作个人主页
  15. TIA protal与SCL从入门到精通(2)——EN/ENO 机制
  16. 课后习题7.11 医院内科有A,B,C,D,E,F,G共7位医生,每人在一周内要值一次夜班,排班的要求是: (1)A医生值班日比C医生晚1天; (2)D医生值班日比E医生晚2天; (3)B医生值班日比
  17. 关于error LNK2005: char * xxx (?xx@@3PADA) already defined in xxx
  18. 下载安装cygwin/X
  19. android 编辑9图片,Android .9.png图片的处理
  20. C printf() 详解之终极无惑

热门文章

  1. mac使用eclipse创建java_【Java】Mac上使用Eclipse创建Java项目的一般步骤
  2. 南非世界杯火热角逐 金山网盾开启搜索保护
  3. 跨媒体检索(关联)之基于CCA的方法大总结
  4. javascript类功能代码集
  5. 百度地图API实现地理围栏
  6. 技术干货|深入理解flannel
  7. MMO移动游戏性能分析报告:渲染、UI、逻辑代码和内存
  8. 【java学习之路】(java框架)011.SpringMVC入门
  9. C语言表达式5 0的结果是,c语言程序设计期末试题B(含答案)(5页).doc
  10. 一个按钮多个ajax,如何为表格中的多个按钮设置AJAX调用