条件变量详细解说

条件变量详细解说1 条件变量概述 条件变量是用来等待线程而不是上锁的 条件变量通常和互斥锁一起使用 条件变量之所以要和互斥锁一起使用 主要是因为互斥锁的一个明显的特点就是它只有两种状态 锁定和非锁定 而条件变量可以通过允许线程阻塞和等待另一个线程发送信号来弥补互斥锁的不足 所以互斥锁和条件变量通常一起使用当条件满足的时候 线程通常解锁并等待该条件发生变化 一旦另一个线程修改了环

1、条件变量概述: 

      条件变量是用来等待线程而不是上锁的,条件变量通常和互斥锁一起使用。条件变量之所以要和互斥锁一起使用,主要是因为互斥锁的一个明显的特点就是它只有两种状态:锁定和非锁定,而条件变量可以通过允许线程阻塞和等待另一个线程发送信号来弥补互斥锁的不足,所以互斥锁和条件变量通常一起使用

       当条件满足的时候,线程通常解锁并等待该条件发生变化,一旦另一个线程修改了环境变量,就会通知相应的环境变量唤醒一个或者多个被这个条件变量阻塞的线程。这些被唤醒的线程将重新上锁,并测试条件是否满足。一般来说条件变量被用于线程间的同步;当条件不满足的时候,允许其中的一个执行流挂起和等待

2、主要应用函数:

pthread_cond_init()函数               功能:初始化一个条件变量

pthread_cond_wait()函数             功能:阻塞等待一个条件变量

pthread_cond_timedwait()函数    功能:限时等待一个条件变量

pthread_cond_signal()函数          功能:唤醒至少一个阻塞在条件变量上的线程

pthread_cond_broadcast()函数    功能:唤醒全部阻塞在条件变量上的线程

pthread_cond_destroy()函数        功能:销毁一个条件变量

以上6 个函数的返回值都是:成功返回0, 失败直接返回错误号。

pthread_cond_t 类型,其本质是一个结构体为简化理解,应用时可忽略其实现细节,简单当成整数看待。如:

pthread_cond_t  cond; 变量cond只有两种取值1、0。

3、函数分析  

<1>、初始化一个条件变量

int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr); 

参2:attr表条件变量属性,通常为默认值,传NULL即可

也可以使用静态初始化的方法,初始化条件变量:

pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

 <2>、阻塞等待一个条件变量

 int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex); 

函数作用:

  1. 阻塞等待条件变量cond(参1)满足
  2. 释放已掌握的互斥锁(解锁互斥量)相当于pthread_mutex_unlock(&mutex);

 1.2.两步为一个原子操作。

     3.当被唤醒,pthread_cond_wait函数返回时,解除阻塞并重新申请获取互斥锁pthread_mutex_lock(&mutex);

<3>、限时等待一个条件变量 

int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime); 

参3: 参看man sem_timedwait函数,查看struct timespec结构体。

struct timespec {

time_t tv_sec; /* seconds */ 秒

long   tv_nsec; /* nanosecondes*/ 纳秒

}

形参abstime:绝对时间。

如:time(NULL)返回的就是绝对时间。而alarm(1)是相对时间,相对当前时间定时1秒钟。

struct timespec t = {1, 0};

pthread_cond_timedwait (&cond, &mutex, &t); 只能定时到 1970年1月1日 00:00:01秒(早已经过去) 

正确用法:

time_t cur = time(NULL); 获取当前时间。

struct timespec t; 定义timespec 结构体变量t

t.tv_sec = cur+1; 定时1秒

pthread_cond_timedwait (&cond, &mutex, &t); 传参 参APUE.11.6线程同步条件变量小节

<4>、唤醒至少一个阻塞在条件变量上的线程  

int pthread_cond_signal(pthread_cond_t *cond); 

<5>、唤醒全部阻塞在条件变量上的线程 

 int pthread_cond_broadcast(pthread_cond_t *cond); 

<6>、销毁一个条件变量 

int pthread_cond_destroy(pthread_cond_t *cond); 

4、条件变量示例 

生产者消费者条件变量模型

       线程同步典型的案例即为生产者消费者模型,而借助条件变量来实现这一模型,是比较常见的一种方法。假定有两个线程,一个模拟生产者行为,一个模拟消费者行为。两个线程同时操作一个共享资源(一般称之为汇聚),生产向其中添加产品,消费者从中消费掉产品。

#include 
  
    #include 
   
     #include 
    
      #include 
     
       //节点结构体 struct msg { int num; //数据区 struct msg *next; //链表区 }; struct msg *head = NULL;//头指针 struct msg *mp = NULL; //节点指针 //利用宏定义的方式初始化全局的互斥锁和条件变量 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t has_product = PTHREAD_COND_INITIALIZER; void *producter(void *arg) { while (1) { mp = malloc(sizeof(struct msg)); mp->num = rand() % 400 + 1; printf("---producted---%d\n", mp->num); pthread_mutex_lock(&mutex);//访问共享区域必须加锁 mp->next = head; head = mp; pthread_mutex_unlock(&mutex); pthread_cond_signal(&has_product);//通知消费者来消费 sleep(rand() % 3); } return NULL; } void *consumer(void *arg) { while (1) { pthread_mutex_lock(&mutex);//访问共享区域必须加锁 while (head == NULL)//如果共享区域没有数据,则解锁并等待条件变量 { pthread_cond_wait(&has_product, &mutex); } mp = head; head = mp->next; pthread_mutex_unlock(&mutex); printf("------------------consumer--%d\n", mp->num); free(mp); //释放被删除的节点内存 mp = NULL;//并将删除的节点指针指向NULL,防止野指针 sleep(rand() % 3); } return NULL; } int main(void) { pthread_t ptid, ctid; //创建生产者和消费者线程 pthread_create(&ptid, NULL, producter, NULL); pthread_create(&ctid, NULL, consumer, NULL); //主线程回收两个子线程 pthread_join(ptid, NULL); pthread_join(ctid, NULL); return 0; } 
      
     
    
  

5、运行结果 

 条件变量详细解说

6、条件变量的优点:

相较于mutex而言,条件变量可以减少竞争。

如直接使用mutex,除了生产者、消费者之间要竞争互斥量以外,消费者之间也需要竞争互斥量,但如果汇聚(链表)中没有数据,消费者之间竞争互斥锁是无意义的。有了条件变量机制以后,只有生产者完成生产,才会引起消费者之间的竞争。提高了程序效率。

更多精彩:

(1403条消息) Qt版本企业级界面_清风徐来Groot的博客-CSDN博客

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/203276.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月19日 下午10:11
下一篇 2026年3月19日 下午10:11


相关推荐

  • ossim mysql密码_OSSIM 4.1安装详解

    ossim mysql密码_OSSIM 4.1安装详解OSSIM4 1 安装详解在今年出版的畅销书 Unix Linux 网络日志分析与流量监控 一书中主要为大家介绍了开源安全运维利器 OSSIM 很多同行对 Ossim 表示了极大关注 纷纷来信咨询如何部署和使用这套系统 下面就 4 1 版的安装方法进行详细说明 具体 ossim 的组成原理大家可参看教程 在安装之前首先确保网络环境能够连接互联 系统会向 debian pool ntp org 站点同步时钟 注意要选

    2026年3月18日
    1
  • js之防抖函数

    js之防抖函数防抖防抖原理事件响应函数在一段时间后才执行 如果这段时间内再次调用 则重新计算执行时间也就是说 在这个时间内 无论你怎么触发事件 我都不会执行 只有这段时间无操作后才会执行 例如 假如防抖设置为 3 秒第一次执行事件后 开始倒计时 3 秒然后等到还剩余 1 秒时 再次触发了事件重新倒计时 从三秒开始这样第一次到第二次触发事件就经历了 5 秒 极大的减轻了服务器的压力先来认识防抖函数的使用 防抖函数实际上已经封装好了 我们只需要用 cdn 引入就可以进行使用了 但是我们依然要明白其中的原理 D

    2026年3月17日
    1
  • javascript 数组以及对象的深拷贝(复制数组或复制对象)的方法

    javascript 数组以及对象的深拷贝(复制数组或复制对象)的方法javascript数组以及对象的深拷贝(复制数组或复制对象)的方法前言在js中,数组和对象的复制如果使用=号来进行复制,那只是浅拷贝。如下图演示:如上,arr的修改,会影响arr2的值,这显然在绝大多数情况下,并不是我们所需要的结果。因此,数组以及对象的深拷贝就是javascript的一个基本功了。数组的深拷贝条条大道通罗马,实现数组的深拷贝,是有好几种方法的。举例如下:for循环

    2022年7月12日
    23
  • java中scanner是什么意思_java中Scanner是什么?怎么用?

    java中scanner是什么意思_java中Scanner是什么?怎么用?在java中有一种类可以获取我们输入的信息,这也是java中的新特征,有小伙伴知道这是什么类吗?小编最近在学Scanner类,发现还需要结合正则表达式一起使用,这对我们以前知识的掌握来说,得有比较好的基础才能完成。下面我们就一起看看Scanner类如何在java中使用吧。1.概念一个可以使用正则表达式来解析基本类型和字符串的简单文本扫描器。2.语法Scanners=newScanner(S…

    2022年7月21日
    16
  • Mybatis查询之resultMap和resultType区别

    Mybatis查询之resultMap和resultType区别结论 resultType 适合使用返回值得数据类型是非自定义的 即 jdk 的提供的类型 resultType 中的内容就是 pojo 在本项目中的位置 当使用 resultType 做 SQL 语句返回结果类型处理时 对于 SQL 语句查询出的字段在相应的 pojo 中必须有和它相同的字段对应 因此对于单表查询的话 resultType 是最合适的 resultMap 适合使用返回值是自定

    2025年6月11日
    4
  • 混元HY-MT1.5-7B教程:API接口设计与性能优化技巧

    混元HY-MT1.5-7B教程:API接口设计与性能优化技巧

    2026年3月12日
    4

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号