深入理解pthread_cond_wait、pthread_cond_signal

===============================man 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来进行条件判断,以保证临界区内只有一个线程在处理。

pthread_cond_wait()  用于阻塞当前线程,等待别的线程使用 pthread_cond_signal() pthread_cond_broadcast来唤醒它   pthread_cond_wait()   必须与pthread_mutex 配套使用。pthread_cond_wait() 函数一进入wait状态就会自动release mutex。当其他线程通过 pthread_cond_signal() pthread_cond_broadcast ,把该线程唤醒,使 pthread_cond_wait()通过(返回)时,该线程又自动获得该mutex 
        pthread_cond_signal 函数的作用是发送一个信号给另外一个正在处于阻塞等待状态的线程,使其脱离阻塞状态,继续执行.如果没有线程处在阻塞等待状态,pthread_cond_signal也会成功返回。
        使用pthread_cond_signal一般不会有“惊群现象”产生,他最多只给一个线程发信号。假如有多个线程正在阻塞等待着这个条件变量的话,那么是根据各等待线程优先级的高低确定哪个线程接收到信号开始继续执行。如果各线程优先级相同,则根据等待时间的长短来确定哪个线程获得信号。但无论如何一个pthread_cond_signal调用最多发信一次。
        但是 pthread_cond_signal 在多处理器上可能同时唤醒多个线程,当你只能让一个线程处理某个任务时,其它被唤醒的线程就需要继续 wait,

==============================另一篇很好的文章===========================

POSIX线程详解

==============================使用效率问题============================

pthread_cond_signal函数的作用是发送一个信号给另外一个正在处于阻塞等待状态的线程,使其脱离阻塞状态,继续执行.如果没有线程处在阻塞等待状态,pthread_cond_signal也会成功返回。
但使用pthread_cond_signal不会有“惊群现象”产生,他最多只给一个线程发信号。假如有多个线程正在阻塞等待着这个条件变量的话,那么是根据各等待线程优先级的高低确定哪个线程接收到信号开始继续执行。如果各线程优先级相同,则根据等待时间的长短来确定哪个线程获得信号。但无论如何一个pthread_cond_signal调用最多发信一次。
另外,互斥量的作用一般是用于对某个资源进行互斥性的存取,很多时候是用来保证操作是一个原子性的操作,是不可中断的
用法:
pthread_cond_wait必须放在pthread_mutex_lock和pthread_mutex_unlock之间,因为他要根据共享变量的状态来决定是否要等待,而为了不永远等待下去所以必须要在lock/unlock队中
共享变量的状态改变必须遵守lock/unlock的规则
pthread_cond_signal即可以放在pthread_mutex_lock和pthread_mutex_unlock之间,也可以放在pthread_mutex_lock和pthread_mutex_unlock之后,但是各有各缺点。
之间:
pthread_mutex_lock
xxxxxxx
pthread_cond_signal
pthread_mutex_unlock
缺点:在某下线程的实现中,会造成等待线程从内核中唤醒(由于cond_signal)然后又回到内核空间(因为cond_wait返回后会有原子加锁的行为),所以一来一回会有性能的问题。
在code review中,我会发现很多人喜欢在pthread_mutex_lock()和pthread_mutex_unlock(()之间调用 pthread_cond_signal或者pthread_cond_broadcast函数,从逻辑上来说,这种使用方法是完全正确的。但是在多线程环境中,这种使用方法可能是低效的。posix1标准说,pthread_cond_signal与pthread_cond_broadcast无需考虑调用线程是否是mutex的拥有者,也就是说,可以在lock与unlock以外的区域调用。如果我们对调用行为不关心,那么请在lock区域之外调用吧。这里举个例子:
       
我们假设系统中有线程1和线程2,他们都想获取mutex后处理共享数据,再释放mutex。请看这种序列:
       
1)线程1获取mutex,在进行数据处理的时候,线程2也想获取mutex,但是此时被线程1所占用,线程2进入休眠,等待mutex被释放。
       
2)线程1做完数据处理后,调用pthread_cond_signal()唤醒等待队列中某个线程,在本例中也就是线程2。线程1在调用pthread_mutex_unlock()前,因为系统调度的原因,线程2获取使用CPU的权利,那么它就想要开始处理数据,但是在开始处理之前,mutex必须被获取,很遗憾,线程1正在使用mutex,所以线程2被迫再次进入休眠。
       
3)然后就是线程1执行pthread_mutex_unlock()后,线程2方能被再次唤醒。
       从这里看,使用的效率是比较低的,如果再多线程环境中,这种情况频繁发生的话,是一件比较痛苦的事情。
但是在LinuxThreads或者NPTL里面,就不会有这个问题,因为在Linux 线程中,有两个队列,分别是cond_wait队列和mutex_lock队列, cond_signal只是让线程从cond_wait队列移到mutex_lock队列,而不用返回到用户空间,不会有性能的损耗。
所以在Linux中推荐使用这种模式。
之后:
pthread_mutex_lock
xxxxxxx
pthread_mutex_unlock
pthread_cond_signal
优点:不会出现之前说的那个潜在的性能损耗,因为在signal之前就已经释放锁了
缺点:如果unlock和signal之前,有个低优先级的线程正在mutex上等待的话,那么这个低优先级的线程就会抢占高优先级的线程(cond_wait的线程),而这在上面的放中间的模式下是不会出现的。
所以,在Linux下最好pthread_cond_signal放中间,但从编程规则上说,其他两种都可以

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/252952.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LeetCode算法题-Factorial Trailing Zeroes(Java实现)

这是悦乐书的第183次更新&#xff0c;第185篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第42题&#xff08;顺位题号是172&#xff09;。给定一个整数n&#xff0c;返回n&#xff01;中的尾随零数。例如&#xff1a; 输入&#xff1a;3 输出&#xff1a;0 说明…

JavaWeb基础—JS学习小结

JavaScript是一种运行在浏览器中的解释型的编程语言 推荐&#xff1a;菜鸟教程一、简介js:javascript是基于对象【哪些基本对象呢】和和事件驱动【哪些主要事件呢】的语言&#xff0c;应用在客户端&#xff08;注意与面向对象的区分&#xff09; js的三大特点&#xff1a;  交…

Asp.Net 设计模式 之 “简单工厂”模式

主要思想&#xff1a;public static Operation CreateFactory(string ope) { //实例化空父类&#xff0c;让父类指向子类 Operation op null; switch (ope) { case "": op …

UBuntu国内镜像地址下载

http://www.oschina.net/p/ubuntu http://releases.ubuntu.com/ http://mirrors.163.com/ubuntu-releases/14.04/

Effective_STL 学习笔记(十九) 了解相等和等价的区别

find 算法和 set 的 insert 成员函数是很多必须判断两个值是否相同的函数代表&#xff0c; find 对 “相同” 的定义是相等&#xff0c;基于 operator &#xff0c; set::insert 对 “相同” 的定义是等价&#xff0c;通常基于 operator< 。 操作上来说&#xff0c;相等的概…

判断是否获取到手机相机权限

实际运用场景&#xff1a; 上传图片&#xff0c;查看相机设备&#xff0c;使用相机 在做这些操作的时候先调用这段话 AVAuthorizationStatus authStatus [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]; if (authStatus AVAuthorizationStatusRestric…

事物笔记

什么是事务&#xff1a; 一件事情有N个组成单元&#xff0c;执行之后要么同时成功&#xff0c;要么同时失败。 MySQL是一条默认的事务&#xff0c;一条sql语句就是一条事务。------------------------------------------------------------MySQL事务&#xff1a; 1、开启一个事…

Python Socket通信黏包问题分析及解决方法

参考&#xff1a;http://www.cnblogs.com/Eva-J/articles/8244551.html#_label5 1.黏包的表现(以客户端远程操作服务端命令为例) 注&#xff1a;只有在TCP协议通信的情况下&#xff0c;才会产生黏包问题 基于TCP协议实现的黏包 #!/usr/bin/env python # -*- coding: utf-8 -*- …

Django 路由

定义&#xff1a; URL配置(URLconf)就像Django 所支撑网站的目录。它的本质是URL与要为该URL调用的视图函数之间的映射表&#xff1b;你就是以这种方式告诉Django&#xff0c;对于这个URL调用这段代码&#xff0c;对于那个URL调用那段代码。 URL配置格式&#xff1a; urlpatter…

Ubuntu默认不进入图形界面

修改 /etc/X11/default-display-manager如果值为/usr/sbin/gdm&#xff0c;(ubuntu12.04 为/usr/sbin/lightdm)则进入图形界面 如果值为false&#xff0c;则进入控制台&#xff08;命令行方式&#xff09;。如果想从控制台进入图形界面&#xff0c;可以在控制台上输入命令 sudo…

读《构建之法》的心得体会

前段时间&#xff0c;我看了《构建之法》的一些内容&#xff0c;有了一些心得体会。 软件工程所讨论的是代码量巨大、涉及人数众多、项目需求多变时所要解决的问题。而在校学生根本就没有这样的环境。而邹欣老师的《构建之法》是我读过的书中最浅显易懂的软件工程书。 在绪论中…

2440内存管理

title: 2440内存管理 tags: ARM date: 2018-10-17 19:08:49 --- 2440内存管理 特性 大/小端&#xff08;通过软件选择&#xff09;地址空间&#xff1a;每个 Bank 有 128M 字节(总共 1G/8 个 Bank)除了 BANK0&#xff08;16/32 位&#xff09;之外【引导ROM&#xff0c;其总线宽…

C#设计模式之十二代理模式(Proxy Pattern)【结构型】

一、引言 今天我们要讲【结构型】设计模式的第七个模式&#xff0c;也是“结构型”设计模式中的最后一个模式&#xff0c;该模式是【代理模式】&#xff0c;英文名称是&#xff1a;Proxy Pattern。还是老套路&#xff0c;先从名字上来看看。“代理”可以理解为“代替”&#…

IPv6检测

1&#xff09;判断服务器是否支持IPv6 &#xff1a; http://ipv6-test.com/validate.php 2&#xff09;检测当前设备打开网站的连接方式是IPv4还是IPv6&#xff1a; http://ipv6.sjtu.edu.cn/ 转载于:https://www.cnblogs.com/superbobo/p/6687605.html

百度首席科学家吴恩达宣布将从百度离职

海外网3月22日电 据媒体消息&#xff0c;百度首席科学家吴恩达&#xff08;Andrew Ng&#xff09;在英文自媒体平台Medium及微博、Twitter等个人社交平台发布公开信&#xff0c;宣布自己将从百度离职&#xff0c;开启自己在人工智能领域的新篇章。 吴恩达是人工智能和机器学习…

CentOS7.5 使用二进制程序部署Kubernetes1.12.2(三)

一、安装方式介绍 1、yum 安装 目前CentOS官方已经把Kubernetes源放入到自己的默认 extras 仓库里面&#xff0c;使用 yum 安装&#xff0c;好处是简单&#xff0c;坏处也很明显&#xff0c;需要官方更新 yum 源才能获得最新版本的软件&#xff0c;而所有软件的依赖又不能自己指…

Oracle存储过程--案例

限额控制 CREATE OR REPLACE PACKAGE BODY NP_PCKG_MERCHANT_LIMIT ASPROCEDURE CHECK_LIMIT (in_iplCode IN VARCHAR2, --行业编号in_iplState IN VARCHAR2, --卡类型in_posNo IN VARCHAR2, --商户号in_tranAmt IN …

SpringMVC—对Ajax的处理(含 JSON 类型)(2)

这里编写了一个通用的类型转换器&#xff1a;用来转换形如&#xff1a; firstNamejack&lastNamelily&gender1&foodsSteak&foodsPizza&quoteEnteryourfavoritequote!&educationJr.High&tOfDDay 到 Student 对象。/*** author solverpeng* create 20…

马来西亚热情拥抱阿里巴巴 马云倡议的eWTP首次落地海外

摘要&#xff1a;3月22日&#xff0c;马来西亚总理纳吉布与阿里巴巴集团董事局主席马云一同出现在吉隆坡一场盛大启动仪式上&#xff0c;他们将共同见证马云的eWTP理念落地马来西亚。 3月22日&#xff0c;在邀请阿里巴巴集团董事局主席马云、阿里巴巴集团CEO张勇、蚂蚁金服集团…

征名公布|Qtum量子链企业版—Unita 中文名征集圆满落幕

Qtum量子链基金会为感谢社区与为了充分调动社区积极性&#xff0c;调动社区力量&#xff0c;在Qtum企业版完整公布之前将中文名留给社区成员们集思广益&#xff0c;其中截止2018年11月26日&#xff0c;我们征集到数百份来自社区的优秀名称&#xff0c;在经过基金会层层严肃认真…