栗子:

mutex.lock();while (判断“条件”是否成立) {pthread_cond_wait 等待
}mutext.unlock();

条件变量这个互斥锁,不是用来保护条件变量的内部状态。而是用来保护外部“条件”(就是那个 while 循环中的判断)。假如条件变量的内部状态需要锁定,完全可以在内部实现中维护一个锁,没有必要从外部传进来。

用于判断是否等待的“条件”,通常就是线程之间共享的某个变量值。A 线程判断“条件”等待,会读取共享变量。B 线程让“条件”满足,会修改变量。这个锁,就是保护线程间的共享变量(“条件”),让“条件”不会一边修改,一边读取。

这把锁还有个额外的作用。wait 函数会阻塞,将线程 A 放到阻塞队列中。wait 函数放在锁的 lock,unlock 内部,也保证了线程还没有真正放到阻塞队列时,线程 B 不能修改“条件”后进行唤醒。这段话应该暂时读不懂,可回头再看,先放在这里,不然描述就不够准确。

不同的应用会有不同的“条件”,在实现条件变量这个库时,是没有办法预先知道这个“条件”究竟是什么。因此要保护“条件”,就只能让用户自己在库外部分配锁。

弄清了这个互斥锁究竟在保护什么。这时我们才能进一步讨论 pthread_cond_wait 为什么要传入这个锁呢?这点就跟 wait 的实现有关。

先来看等待线程 A 的写法。

mutex.lock();while (判断“条件”是否成立) {pthread_cond_wait 等待
}mutext.unlock();

判断“条件”是否成立,在 lock, unlock 之间受到保护。pthread_cond_wait 可能会阻塞,假如真的阻塞,mutex 这锁就一直不能被释放了。因此在 pthread_cond_wait 的实现内部,在阻塞之前,必须先将锁释放。在唤醒的时候,再重新获取锁。

pthread_cond_wait 展开,内部实现中,会有下面的过程。

做一些其它事情
mutext.unlock()
阻塞中...
唤醒
mutext.lock()

假如在线程 A 中完全展开 pthread_cond_wait

mutex.lock();while (判断“条件”是否成立) {做一些其它事情mutext.unlock()阻塞唤醒mutext.lock()
}mutext.unlock();

可以看到,判断“条件” 一直在 mutext 的 lock, unlock 之间,受到保护。

来到这里,我们总结一下。

  1. 跟条件变量关联的互斥锁,是用于保护判断“条件”,并非条件变量的内部状态。
  2. 将互斥锁,传入 wait 的实现中,是让条件变量库可以在适当的时机释放(unlock)和重新获取(lock)这把锁。

注意上面第 2 点中,“适当时机”这个词。假如不将这个锁传入 pthread_cond_wait,让用户自己在阻塞前先释放锁,是没有办法做到适当时机的。阻塞队列在条件变量库内部维护,只有条件变量库内部才能控制这个时机。

wait 会阻塞,将线程 A 放到阻塞队列中。在线程还没有真正放到阻塞队列时,需要一直上锁。不然另一线程 B 修改“条件”后进行唤醒,这个线程 A 还没有在阻塞队列中,就不能被唤醒了。因而这把保护外部判断“条件”的锁,需要传入到 wait 函数中。等将线程真正放到阻塞队列后才能解锁,之后线程被唤醒后再重新获取。

线程 A 的 pthread_cond_wait 需要传入 mutext,并且 pthread_cond_wait 一定要在 lock 和 unlock 之间。

至于线程 B, 用于修改“条件”。修改“条件” 也必须在 lock 和 unlock 之间,受互斥锁保护。至于 pthread_cond_signal 是否在 lock 和 unlock 之间,其实是没有关系的。但 pthread_cond_signal 必须在修改“条件”之后,不然可能 A 线程被唤醒后,条件不满足继续等待,这个唤醒信号就丢失了。

mutext.lock()
修改”条件“
pthread_cond_signal 唤醒
mutext.unlock()

也可以将 pthread_cond_signal 移到 unlock 外部。

mutext.lock()
修改”条件“
mutext.unlock()
pthread_cond_signal 唤醒

条件变量等待时,那个 wait 的判断,需要使用 while 循环,而不是 if,是防止虚假唤醒。就不再一一分析了。

作者:黄兢成
链接:https://www.zhihu.com/question/24116967/answer/676751734
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

(SAW:Game Over!)

OS / Linux / pthread_cond_wait 为什么需要传递 mutex 参数?相关推荐

  1. u-boot向linux内核传递启动参数

    U-BOOT 在启动内核时,会向内核传递一些参数.而这些参数是通过 structtag来传递的. U-boot 把要传递给 kernel 的东西保存在 struct tag数据结构中,启动 kerne ...

  2. liunx 上get 不到url参数 java_URL传递中文参数,大坑一枚,Windows与Linux效果竟然不一致...

    下午,计划2个小时搞定,个人官网第6次升级,就可以干点轻松的事了,结果,下午多搞了2个小时,晚上又搞了2个小时,才搞定.最后一个世界难题是,URL传递中文参数.问题大致是这么出现的:我为"博 ...

  3. linux中probe函数传递参数的寻找(下)

    点击打开链接 linux中probe函数传递参数的寻找(下) 通过追寻driver的脚步,我们有了努力的方向:只有找到spi_bus_type的填充device即可,下面该从device去打通,当两个 ...

  4. linux中probe函数中传递的参数来源(上)

    点击打开链接 上一篇中,我们追踪了probe函数在何时调用,知道了满足什么条件会调用probe函数,但probe函数中传递的参数我们并不知道在何时定义,到底是谁定义的,反正不是我们在驱动中定义的(当然 ...

  5. linux下c语言线程传参数,【linux】C语言多线程中运行线程池,在线程池中运行线程池,,传递的结构体参数值为空/NULL/0...

    C语言多线程中运行线程池,在线程池中运行线程池,,传递的结构体参数值为空/NULL/0 本贴问题,之前已经提问过一次,当时已经解决了,原贴在这里https://segmentfault.com/q/1 ...

  6. Linux基本命令之date命令的参数及获时间戳的方法

    这篇文章主要为大家介绍了Linux基本命令中的date命令的参数及获时间戳的方法,在本文中列举了4个获取时间戳的方法,需要的朋友可以参考下 1.时间命令:date 向date命令传递参数适用'+'(加 ...

  7. 使用ajax发送数组请求,Ajax请求传递数组参数

    var ids = []; var rows=$("#tt").datagrid("getSelections"); for(var i=0; i ids.pu ...

  8. linux 内核学习11-内核模块参数

    linux 内核学习11-内核模块参数 内核模块作为一个可拓展的动态模块,为Linux内核提供灵活性,所以需要依据不同的场景来传递不同的参数,实现不同的功能 1. 准备工作 #define modul ...

  9. OS + linux command / Linux Command / Linux command / linux Command

    写下你职业生涯中最难以忘怀的误操作.. http://www.dangkai.com/ArticlePage/Article59549.htm http://bbs.chinaunix.net/thr ...

最新文章

  1. 详解VirtualBox虚拟机网络环境解析和搭建-NAT、桥接、Host-Only、Internal、端口映射
  2. 【M25】将构造方法和非成员方法虚化
  3. 说学习前端开发简单,如何才能成功上岸?
  4. [转载] 如何用python实现一行两个输入
  5. EMNLP 2021 | 罗氏和博阿齐奇大学研究合作团队提出:多标签文本分类中长尾分布的平衡策略...
  6. vue $slot基本用法
  7. 推荐十大国外IT网站
  8. java svg等值线_带有颜色填充的等值线画法研究
  9. Windows 10 未安装任何音频输出设备 解决方案
  10. 【分享】地产集团公司LOGO设计
  11. STM32H750移植STemWin,驱动ST7789
  12. 强化学习——股票预测项目复现
  13. Vue2.0 —— 运用算法实现 AST 抽象语法树
  14. PHPMyWind5.4存储XSS(CVE-2017-12984)
  15. 软件测试知识点和面试题--手工测试篇(功能测试)
  16. Drawio免费绘图工具
  17. 选择java大数据开发方向学习,应该怎么规划学习路线
  18. 电脑检查内存条型号的方法
  19. 为docker设置国内镜像
  20. oracle查询job号,oracle job号使用

热门文章

  1. express4.x中的链式路由句柄
  2. iptables实现网络防火墙及地址转换
  3. 转载收藏之用 - 微信公众平台开发教程(三):微信公众平台开发验证
  4. VoIP安全问题解析
  5. 【实用技能】通过sh脚本动态上传项目到github
  6. 2020 操作系统第一天复习(习题总结)
  7. linux deepin “debconf: DbDriver “config“: config.dat 被另一个进程锁定:资源暂时不可用“
  8. minio扩展现有的分布式集群:扩大集群规模,增加磁盘数量
  9. Hadoop Hive导入数据命令
  10. Flink流计算WordCount代码示例