互斥量、条件变量与pthread_cond_wait()函数的使用,详解(一)

2023-05-16

1. 首先pthread_cond_wait 的定义是这样的

The pthread_cond_wait() and pthread_cond_timedwait() functions are used to block on a condition variable. They are called with mutex locked by the calling thread or undefined behaviour will result.

These functions atomically release mutex and cause the calling thread to block on the condition variable cond ; atomically here means "atomically with respect to access by another thread to the mutex and then the condition variable". That is, if another thread is able to acquire the mutex after the about-to-block thread has released it, then a subsequent call topthread_cond_signal() or pthread_cond_broadcast() in that thread behaves as if it were issued after the about-to-block thread has blocked.

2. 由上解释可以看出,pthread_cond_wait() 必须与pthread_mutex 配套使用。

pthread_cond_wait()函数一进入wait状态就会自动release mutex.

In Thread1:

pthread_mutex_lock(&m_mutex);   
pthread_cond_wait(&m_cond,&m_mutex);   
pthread_mutex_unlock(&m_mutex);  

In Thread2:

pthread_mutex_lock(&m_mutex);   
pthread_cond_signal(&m_cond);   
pthread_mutex_unlock(&m_mutex);  

为什么要与pthread_mutex 一起使用呢? 这是为了应对线程1在调用pthread_cond_wait()但线程1还没有进入wait cond的状态的时候,此时线程2调用了 cond_singal 的情况。 如果不用mutex锁的话,这个cond_singal就丢失了。加了锁的情况是,线程2必须等到 mutex 被释放(也就是 pthread_cod_wait() 进入wait_cond状态 并自动释放mutex) 的时候才能调用cond_singal.

3. pthread_cond_wait() 一旦wait成功获得cond 条件的时候会自动 lock mutex.

这就会出现另一个问题。这是因为

The pthread_cond_wait() and pthread_cond_timedwait() is a cancellation point.

In Thread3:

pthread_cancel(&m_thread);

pthread_join();

因为pthread_cond_wait() and pthread_cond_timedwait()    是线程退出点函数,因此在Thread3中

可以调用pthread_cancel()来退出线程1。那样显然线程1会在 pthread_cond_wait(&m_cond,&m_mutex);    和 pthread_mutex_unlock(&m_mutex); 之间退出,    pthread_cond_wait() 函数返回后自动lock住了mutex, 这个时候线程1退出(并没有运行到pthread_mutex_unlock()),如果Thread2这个时候就再也得不到lock状态了。

通常解决这个问题的办法如下

void cleanup(void *arg)
{
   pthread_mutex_unlock(&mutex);
}
void * thread1(void * arg)
{
    pthread_cleanup_push(cleanup, NULL); // thread cleanup handler 
    pthread_mutex_lock(&mutex);
   pthread_cond_wait(&cond, &mutex);
   pthread_mutex_unlock(&mutex);
   pthread_cleanup_pop(0 );
}

LINUX环境下多线程编程肯定会遇到需要条件变量的情况,此时必然要使用pthread_cond_wait()函数。但这个函数的执行过程比较难于理解。

LINUX环境下多线程编程肯定会遇到需要条件变量的情况,此时必然要使用pthread_cond_wait()函数。但这个函数的执行过程比较难于理解。
    pthread_cond_wait()的工作流程如下(以MAN中的EXAMPLE为例):
       Consider two shared variables x and y, protected by the mutex mut, and a condition vari-
       able cond that is to be signaled whenever x becomes greater than y.

              int x,y;
              pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
              pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

       Waiting until x is greater than y is performed as follows:

              pthread_mutex_lock(&mut);
              while (x <= y) {
                      pthread_cond_wait(&cond, &mut);
              }
              /* operate on x and y */
              pthread_mutex_unlock(&mut);

       Modifications on x and y that may cause x to become greater than y should signal the con-
       dition if needed:

              pthread_mutex_lock(&mut);
              /* modify x and y */
              if (x > y) pthread_cond_broadcast(&cond);
              pthread_mutex_unlock(&mut);

     这个例子的意思是,两个线程要修改X和 Y的值,第一个线程当X<=Y时就挂起,直到X>Y时才继续执行(由第二个线程可能会修改X,Y的值,当X>Y时唤醒第一个线程),即 首先初始化一个普通互斥量mut和一个条件变量cond。之后分别在两个线程中分别执行如下函数体:

                pthread_mutex_lock(&mut);
              while (x <= y) {
                      pthread_cond_wait(&cond, &mut);
              }
              /* operate on x and y */
              pthread_mutex_unlock(&mut);

和:       pthread_mutex_lock(&mut);
              /* modify x and y */
              if (x > y) pthread_cond_signal(&cond);
              pthread_mutex_unlock(&mut);
 
    其实函数的执行过程非常简单,在第一个线程执行到pthread_cond_wait(&cond,&mut)时,此时如果X<=Y,则此函数就将mut互斥量解锁 ,再将cond条件变量加锁 ,此时第一个线程挂起 (不占用任何CPU周期)。
    而在第二个线程中,本来因为mut被第一个线程锁住而阻塞,此时因为mut已经释放,所以可以获得锁mut,并且进行修改X和Y的值,在修改之后,一个IF语句判定是不是X>Y,如果是,则此时pthread_cond_signal()函数会唤醒第一个线程 ,并在下一句中释放互斥量mut。然后第一个线程开始从pthread_cond_wait()执行,首先要再次锁mut , 如果锁成功,再进行条件的判断 (至于为什么用WHILE,即在被唤醒之后还要再判断,后面有原因分析),如果满足条件,则被唤醒 进行处理,最后释放互斥量mut 。

 

    至于为什么在被唤醒之后还要再次进行条件判断(即为什么要使用while循环来判断条件),是因为可能有“惊群效应”。有人觉得此处既然是被唤醒的,肯定 是满足条件了,其实不然。如果是多个线程都在等待这个条件,而同时只能有一个线程进行处理,此时就必须要再次条件判断,以使只有一个线程进入临界区处理。 对此,转来一段:

引用下POSIX的RATIONALE: 

Condition Wait Semantics 

It is important to note that when pthread_cond_wait() and pthread_cond_timedwait() return without error, the associated predicate may still be false. Similarly, when pthread_cond_timedwait() returns with the timeout error, the associated predicate may be true due to an unavoidable race between the expiration of the timeout and the predicate state change. 

The application needs to recheck the predicate on any return because it cannot be sure there is another thread waiting on the thread to handle the signal, and if there is not then the signal is lost. The burden is on the application to check the predicate. 

Some implementations, particularly on a multi-processor, may sometimes cause multiple threads to wake up when the condition variable is signaled simultaneously on different processors. 

In general, whenever a condition wait returns, the thread has to re-evaluate the predicate associated with the condition wait to determine whether it can safely proceed, should wait again, or should declare a timeout. A return from the wait does not imply that the associated predicate is either true or false. 

It is thus recommended that a condition wait be enclosed in the equivalent of a "while loop" that checks the predicate. 

从上文可以看出: 
1,pthread_cond_signal在多处理器上可能同时唤醒多个线程,当你只能让一个线程处理某个任务时,其它被唤醒的线程就需要继续 wait,while循环的意义就体现在这里了,而且规范要求pthread_cond_signal至少唤醒一个pthread_cond_wait上 的线程,其实有些实现为了简单在单处理器上也会唤醒多个线程. 
2,某些应用,如线程池,pthread_cond_broadcast唤醒全部线程,但我们通常只需要一部分线程去做执行任务,所以其它的线程需要继续wait.所以强烈推荐此处使用while循环.

       其实说白了很简单,就是pthread_cond_signal()也可能唤醒多个线程,而如果你同时只允许一个线程访问的话,就必须要使用while来进行条件判断,以保证临界区内只有一个线程在处理。

 

转贴:http://hi.baidu.com/yangyangye2008/blog/item/ee7bb186f8d3cd27c75cc37f.html


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

互斥量、条件变量与pthread_cond_wait()函数的使用,详解(一) 的相关文章

  • 关于C-V2X 你需要知道的十件事

    蜂窝车联网 xff0c 通信正持续获得生态系统的支持 xff0c 将成为对汽车安全和未来自动驾驶至关重要的一项技术 在整个汽车和科技行业也都能看到C V2X技术的发展势头 举例来说 xff0c 5G汽车联盟 xff08 5GAA xff09
  • C++中的四种强制转换 dynamic_case,const_cast,static_case,reinterprer_case的不同

    使用标准C 43 43 的类型转换符 xff1a static cast dynamic cast reinterpret cast 和const cast 1 static cast 用法 xff1a static cast lt typ
  • V2X高通的布局

  • 5W2H工作法,使工作更有条理,生活更好梳理

    发明者用五个以W开头的英语单词和两个以H开头的英语单词进行设问 xff0c 发现解决问题的线索 xff0c 寻找发明思路 xff0c 进行设计构思 xff0c 从而搞出新的发明项目 xff0c 这就叫做5W2H法 xff08 1 xff09
  • C 可变参数

    有时 xff0c 您可能会碰到这样的情况 xff0c 您希望函数带有可变数量的参数 xff0c 而不是预定义数量的参数 C 语言为这种情况提供了一个解决方案 xff0c 它允许您定义一个函数 xff0c 能根据具体的需求接受可变数量的参数
  • 给初学者:3个月学会机器学习 ||附完整路径+资源

    感觉本科学的三门数学课 xff0c 不是无用的鸡肋了 xff0c 可是我已经都还给老师了 http www sohu com a 225511837 99905135 https www jianshu com p 27124019c69b
  • 车路协调场景与演进与V2X SDK技术解析

    车路协调场景与演进与V2X SDK技术解析 xff1a 回看链接 https apposcmf8kb5033 h5 xiaoeknow com content page eyJ0eXBlIjoiMiIsInJlc291cmNlX3R5cGU
  • 新的开始之Win7、CentOS 6.4 双系统 硬盘安装

    目的 xff1a 在已经有Win7的操作系统上安装CentOS6 4的32位操作系统 本博客结合了以下的博客 http blog csdn net markho365 article details 8969591 http www cnb
  • 详解protobuf-从原理到使用

    这里写的少 xff0c 后面再补充 https www jianshu com p 419efe983cb2
  • signal(SIGCHLD, SIG_IGN)和signal(SIGPIPE, SIG_IGN);

    这个链接写的比较好 xff1a https yq aliyun com articles 42215 signal SIGCHLD SIG IGN 因为并发服务器常常fork很多子进程 xff0c 子进程终结之后需要服务器进程去wait清理
  • Linux c 网络socket编程

    网络编程 xff0c 一定离不开套接字 xff1b 那什么是套接字呢 xff1f 在Linux下 xff0c 所有的I O操作都是通过读写文件描述符而产生的 xff0c 文件描述符是一个和打开的文件相关联的整数 xff0c 这个文件并不只包
  • Linux c 下socket编程全面

    网络的Socket数据传输是一种特殊的I O xff0c Socket也是一种文件描述符 Socket也具有一个类似于打开文件的函数调用Socket xff0c 该函数返回一个整型的Socket描述符 xff0c 随后的连接建立 数据传输等
  • CANoe与金溢的obu can连接的环境问题 Cifconfig can0 up 失败 设置波特率失败

    今天搭建了CANoe与金溢的obu can连接的环境问题 遇到了一个让人不解的问题 can0起不来 xff0c 于是怀疑波特率不匹配 xff0c 使用调不了 Linux 设置波特率 ifconfig can0 down 关闭CAN0 ip
  • V2X-Locate方案,解决隧道内自动车辆定位问题

    2019年3月连网自驾车辆 Connected and Autonomous Vehicles xff0c CAV 通讯技术厂商CohdaWireless于挪威B rum市新建隧道 长达1 3英里 2 2公里 内采用V2X Locate方案
  • 一个高级软件工程师面试被问的问题

    使用new和malloc如何解决内存碎片问题 xff1f 多进程间通信几种方式 xff0c 你用过几种方式 xff1f 线程间通信 xff0c 用过几种方式 分不同的场景 xff0c 适合用哪种通信方式 内存管理 xff0c 如果让你来实现
  • 面试时你需要问HR什么问题?

    与职位相关的问题要多问 xff0c 如 xff1a 1 我知道该职位的首要职责 xff0c 但公司有没有其他的要求 xff1f 2 我的专长是XX xff0c 请问XX部门在公司占有什么样的位置 xff1f 6 面试之后的安排都是什么 xf
  • Linux中断实现浅析

    本文描述内容针对2 6 31 43 x86平台 xff0c 不包含硬件相关的细节 作者 xff1a 独孤九贱 xff1b 版权所有 xff0c 转载请注明出处 有问题欢迎与我交流讨论 一 概述 中断 xff0c 本质上是一个电信号 xff0
  • Linux常用命令

    应用patch patch p1 lt test1 patch 卸载patch patch Rp1 lt test1 patch
  • 【jetson nano、NX、TX2 开机自启动程序,开机自动解除硬件限制,开机默认最大性能工作。】

    jetson nano NX TX2 开机自启动程序 xff0c 开机自动解除硬件限制 xff0c 开机默认最大性能工作 jetson nano NX TX2是英伟达开发的边缘平台 xff0c 其良好的性能 亲民的价格非常适合部署深度学习模
  • 蒙特卡洛法(一)

    蒙特卡洛法也成为统计模拟方法 xff0c 通过从概率模型的随机抽样进行近似数值计算的方法 马尔科夫链蒙特卡洛法则是以马尔科夫链为概率模型的蒙特卡洛法 xff0c 构建一个马尔科夫链 xff0c 使其平稳分布就是要进行抽样的分布 xff0c

随机推荐

  • 芯片测试术语 ,片内测试(BIST),ATE测试

    芯片测试分为如下几类 xff1a 1 WAT xff1a Wafer AcceptanceTest xff0c wafer level 的管芯或结构测试 xff1b 2 CP xff1a chip probing xff0c wafer l
  • SBUS协议及编解码

    1 简介 SBUS本质是一种串口通信协议 xff0c 采用100K的波特率 xff0c 8位数据位 xff0c 两位停止位 xff0c 偶效验 xff0c 即8E2的串口通信 值得注意的有三点 xff1a 1 SBUS采用负逻辑 xff0c
  • Linux内核跨模块函数调用:EXPORT_SYMBOL()宏定义

    一 查看内核驱动代码你会发现很多的函数带有EXPORT SYMBOL 宏定义 二 那么EXPORT SYMBOL的作用是什么 xff1f EXPORT SYMBOL标签内定义的函数或者符号对全部内核代码公开 xff0c 不用修改内核代码就可
  • linux内核I2C子系统详解

    1 I2C通信协议 参考博客 xff1a I2C通信协议详解和通信流程分析 xff1b https csdnimg cn release blogv2 dist pc themesSkin skin3 template images bg
  • 内核驱动中断申请类型及函数分析

    ret 61 request irq chip gt irq xxx intr handler IRQF TRIGGER FALLING IRQF NO THREAD IRQF NO SUSPEND name chip 上面是中断初始化中调
  • I2C设备注册的4种方法

    文章目录 前言一 静态注册二 动态注册三 用户空间注册四 i2c驱动扫描注册 前言 I2C设备的4种添加方法 xff1a 1 xff09 静态注册 2 xff09 动态注册 3 xff09 用户空间注册 4 xff09 i2c驱动扫描注册
  • pm_wakeup.h

    pm wakeup h Power management wakeup interface Copyright C 2008 Alan Stern Copyright C 2010 Rafael J Wysocki Novell Inc T
  • GTK+ Reference Manual

    GTK 43 Reference Manual for GTK 43 2 6 2 Table of Contents I GTK 43 Overview Compiling the GTK 43 libraries How to compi
  • Linux获取进程列表

    实现思路是 xff1a 遍历 proc目录下的所有进程描述文件夹 xff0c 从而获取进程列表 代码如下 xff1a include lt stdio h gt include lt dirent h gt include lt unist
  • ubuntu18.04 下firefox 不能 播放视频,因为默认未安装FLASH插件。(当然只是原因之一)

    ubuntu18 04 下firefox 不能 播放视频 xff0c 默认未安装FLASH插件 终端输入 xff1a sudo apt get install flashplugin nonfree
  • Ubuntu上可使用的15个桌面环境

    Ubuntu上可使用的15个桌面环境 发布者 红黑魂 来自 Ubuntu之家 摘要 Linux下桌面环境很多 xff0c Ubuntu之家给大家总结了比较常用的15个桌面环境 xff0c 并附上Ubuntu 12 10 xff08 Linu
  • C语言数据类型

    数据类型在数据结构中的定义是一个值的集合以及定义在这个值集上的一组操作 数据类型包括原始类型 多元组 记录单元 代数数据类型 抽象数据类型 参考类型以及函数类型 本文主要以51单片机中的数据类型为中心而展开的话题 在keil C51或者ia
  • 《Cortex-M0权威指南》之Cortex-M0技术综述

    Cortex M0权威指南 之Cortex M0技术综述 转载请注明来源 xff1a cuixiaolei的技术博客 Cortex M0 处理器简介 1 Cortex M0 处理器基于冯诺依曼架构 xff08 单总线接口 xff09 xff
  • xos详解5:PendSV_Handler

    PendSV Handler PendSV Handler LDR R2 61 OSTcbCurr 不必关中断 嵌套中断发生时会自动保存 R0 R3 到 MSP 并恢复 LDR R0 R2 如果发生咬尾的多个 PendSV xff0c 上半
  • M0最高优先级的中断设计

    1 Reset 3 Highest Reset 绝大部分处理器设计时 xff0c 将复位中断放在最高优先级 一般来说这样设计是合理的 xff0c 个人认为在某些应用场景这样处理仍有局限性 2 NMI 2 Nonmaskable interr
  • 如何从零开始写一个操作系统?

    首页发现等你来答 登录加入知乎 如何从零开始写一个简单的操作系统 xff1f 关注问题 写回答 操作系统 编程学习 如何从零开始写一个简单的操作系统 xff1f 看了这个 xff1a 从零开始写一个简单的操作系统 求指教 关注者 4 787
  • 每次听到同事跳槽后的薪资,我就像打了鸡血一样

    本文总结了现阶段 34 大龄程序员 34 的职业生存状况 xff0c 内容包含职位需求量 xff0c 议价能力如何以及如何度过传说中的 34 中年危机 34 等等 xff0c 供大家参考 xff01 值此金 三 银四跳槽季 的开端 xff0
  • Lua性能优化—Lua内存优化

    原文链接https blog uwa4d com archives usparkle luaperformance html 这是侑虎科技第236篇原创文章 xff0c 感谢作者舒航供稿 xff0c 欢迎转发分享 xff0c 未经作者授权请
  • Jetson Xavier NX(emmc)烧录系统时可能遇到的问题(避坑)

    目录 Ununtu18虚拟机无法联网 当NX接上电源后 xff0c 指示灯没有亮 xff08 不工作 xff09 在登陆SDK时 xff0c 可能会出现卡在初始界面的情况 在烧录镜像时 xff0c 可能会卡在该处没有变化 Ununtu18虚
  • 互斥量、条件变量与pthread_cond_wait()函数的使用,详解(一)

    1 首先pthread cond wait 的定义是这样的 The pthread cond wait and pthread cond timedwait functions are used to block on a conditio