pthread_cond_wait()使用_~烨的博客-程序员秘密

技术标签: Linux  pthread_cond_wait  

pthread_cond_wait总和一个互斥锁结合使用。在调用pthread_cond_wait前要先获取锁。pthread_cond_wait函数执行时先自动释放指定的锁,然后等待条件变量的变化。在函数调用返回之前,自动将指定的互斥量重新锁住。

int pthread_cond_signal(pthread_cond_t * cond);

pthread_cond_signal通过条件变量cond发送消息,若多个消息在等待,它只唤醒一个。pthread_cond_broadcast可以唤醒所有。调用pthread_cond_signal后要立刻释放互斥锁,因为pthread_cond_wait的最后一步是要将指定的互斥量重新锁住,如果pthread_cond_signal之后没有释放互斥锁,pthread_cond_wait仍然要阻塞。

无论哪种等待方式,都必须和一个互斥锁配合,以防止多个线程同时请求pthread_cond_wait()(或pthread_cond_timedwait(),下同)的竞争条件(Race Condition)。mutex互斥锁必须是普通锁(PTHREAD_MUTEX_TIMED_NP)或者适应锁 (PTHREAD_MUTEX_ADAPTIVE_NP),且在调用pthread_cond_wait()前必须由本线程加锁 (pthread_mutex_lock()),而在更新条件等待队列以前,mutex保持锁定状态,并在线程挂起进入等待前解锁。在条件满足从而离开 pthread_cond_wait()之前,mutex将被重新加锁,以与进入pthread_cond_wait()前的加锁动作对应。

激发条件有两种形式,pthread_cond_signal()激活一个等待该条件的线程,存在多个等待线程时按入队顺序激活其中一个;而pthread_cond_broadcast()则激活所有等待线程。

    下面是另一处说明:给出了函数运行全过程。 为什么在唤醒线程后要重新mutex加锁?
了解 pthread_cond_wait() 的作用非常重要 – 它是 POSIX 线程信号发送系统的核心,也是最难以理解的部分。

首先,让我们考虑以下情况:线程为查看已链接列表而锁定了互斥对象,然而该列表恰巧是空的。这一特定线程什么也干不了 – 其设计意图是从列表中除去节点,但是现在却没有节点。因此,它只能:

锁定互斥对象时,线程将调用 pthread_cond_wait(&mycond,&mymutex)。pthread_cond_wait() 调用相当复杂,因此我们每次只执行它的一个操作。

pthread_cond_wait() 所做的第一件事就是同时对互斥对象解锁(于是其它线程可以修改已链接列表),并等待条件 mycond 发生(这样当 pthread_cond_wait() 接收到另一个线程的“信号”时,它将苏醒)。现在互斥对象已被解锁,其它线程可以访问和修改已链接列表,可能还会添加项。 【要求解锁并阻塞是一个原子操作】

此时,pthread_cond_wait() 调用还未返回。对互斥对象解锁会立即发生,但等待条件 mycond 通常是一个阻塞操作,这意味着线程将睡眠,在它苏醒之前不会消耗 CPU 周期。这正是我们期待发生的情况。线程将一直睡眠,直到特定条件发生,在这期间不会发生任何浪费 CPU 时间的繁忙查询。从线程的角度来看,它只是在等待 pthread_cond_wait() 调用返回。

现在继续说明,假设另一个线程(称作“2 号线程”)锁定了 mymutex 并对已链接列表添加了一项。在对互斥对象解锁之后,2 号线程会立即调用函数 pthread_cond_broadcast(&mycond)。此操作之后,2 号线程将使所有等待 mycond 条件变量的线程立即苏醒。这意味着第一个线程(仍处于 pthread_cond_wait() 调用中)现在将苏醒。

现在,看一下第一个线程发生了什么。您可能会认为在 2 号线程调用 pthread_cond_broadcast(&mymutex) 之后,1 号线程的 pthread_cond_wait() 会立即返回。不是那样!实际上,pthread_cond_wait() 将执行最后一个操作:重新锁定 mymutex。一旦 pthread_cond_wait() 锁定了互斥对象,那么它将返回并允许 1 号线程继续执行。那时,它可以马上检查列表,查看它所感兴趣的更改。

来看一个例子(你是否能理解呢?):


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状态 ,此时线程2上锁) 的时候才能调用cond_singal.

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返回后会有原子加锁的 行为),所以一来一回会有性能的问题。但是在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的线程),而这在上面的放中间的模式下是不会出现的。


转自:https://blog.csdn.net/brian12f/article/details/73882166?utm_source=copy

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/euyy1029/article/details/82840726

智能推荐

numpy-创建一个一维数组_numpy定义一维数组_一个IT小白的博客-程序员秘密

数组中的数据类型,下面实例可以看出优先级规则是:string > float > int

ClassNotFoundException: org.springframework.web.context.ContextLoaderListene_HaHadoop的博客-程序员秘密

今天学习spring+cxf的时候遇到一个问题:在web.xml中配置了spring的上下文监听器:Xml代码  listener>      listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>  listener>   随后启动tom

urllib库安装_urllib库怎么安装_好好爱自己1的博客-程序员秘密

问题:urllib3安装1、python3 pip install urllib安装错误信息: Could not find a version that satisfies the requirement urllib (from versions)No matching d...

【大厂面试】今日头条前端面试17问_柯晓楠的博客-程序员秘密

对async、await的理解,内部原理介绍下Promise,内部实现清除浮动定位问题(绝对定位、相对定位等)从输入URL到⻚面加载全过程tcp3次握手tcp属于哪一层(1 物理层 -> 2 数据链路层 -> 3 网络层(ip)-> 4 传输层(tcp) -> 5 应用层(http))redux的设计思想接入redux的过...

基于kali Linux 的 OpenVAS【Greenbone】虚机 镜像_东方闲仁的博客-程序员秘密

基于kali Linux 的 openvas【greenbone】虚机 镜像新版openvas 切换为gvm后,操作命令有变化,摸索装好后,做了虚机镜像,有需要的请下载试用。链接: https://pan.baidu.com/s/1pWgHUqquCH6tfHEtUDSvUQ 提取码: yxib【OpenVAS是开放式漏洞评估系统】Greenbone Security Assistant 绿骨头安全助理。基于kali Linux 系统的虚机镜像东方闲仁...

座机拨打电话_weixin_34128237的博客-程序员秘密

1.北京座机拨打外地电话前面加90;2北京拨打本地电话前面加9;转载于:https://www.cnblogs.com/mmnyjq/p/3928921.html

随便推点

等距离股票配对量化交易策略-R语言_r语言中trading.signals_SuShiGongMao的博客-程序员秘密

为了分散交易中的风险,我们可以在量化交易中选择市场中性策略。我们选择具有基本面相关性的股票对,价格趋势具有趋同和趋离,买多强势的股票,卖空弱势的股票,同时交易,控制股市风险。案例是石油公司埃克森美孚(NYSE:XOM)和石油公司雪佛龙(NYSE:CVX)公司简介:埃克森美孚公司于1882年在美国新泽西州成立,公司的分部及附属公司在美国和世界上大多数其他国家经营或买卖产品。公司主要业务为能源...

TreeSet比较器的用法_treeset外部比较器的用法_我只想把名字改成拂晓神剑的博客-程序员秘密

TreeSet> ts = new TreeSet>(new Comparator>() { @Override public int compare(Map m2, Map m1) { // TODO Auto-generated method stub int i1 = Integer.parseInt(m1.get("all")); int i2 = In

一文让你搞懂面向对象设计原则(单一职责原则,开闭原则,里氏代换原则,依赖倒转原则,接口隔离原则,合成复用原则,迪米特法则)_用一个案例说明单一职责和迪米特_加油,进大厂!!!的博客-程序员秘密

面向对象设计原则 可维护性:指软件能够被理解,改正,适应及扩展的难易程度。 可复用性:指软件能够被重复使用的难易程度。 面向对象设计的目标之一在于支持可维护性服用,一方面需要实现设计方案或者源代码的服用,另一方面 要确保系统能够易于扩展和修改,具有良好的可维护性。 面向对象设计原则为支持可维护性服用而诞生 指导性原则,非强制性原则。 每一个设计模式都符合一个或多个面向对象设计原则,面向对象设计原则是用于评价 一个设计模式的使用效果的重要指标之一。面向对象设计原则概

【bug】【docker】IPv4 forwarding is disabled. Networking will not work_lorogy的博客-程序员秘密

centos7 + dockervi /etc/sysctl.conf添加代码:net.ipv4.ip_forward=1重启network服务:systemctl restart network && systemctl restart docker查看是否修改成功:sysctl net.ipv4.ip_forward

SQL Server CONVERT() 函数_sqlserver convert_菜鸟界的菜鸟的博客-程序员秘密

定义和用法CONVERT() 函数是把日期转换为新数据类型的通用函数。CONVERT() 函数可以用不同的格式显示日期/时间数据。语法CONVERT(data_type(length),expression,style)值 描述 data_type(length) 规定目标数据类型(带有可选的长度)。 expression 规定需要转换的值。 style 规定日期/时间的输出格式。 下面的表格展示了 datetime 或 smalldatet...