当前位置: 首页 > news >正文

【多线程】五、线程同步 条件变量

文章目录

  • Ⅰ. 线程同步概念
  • Ⅱ. 条件变量
    • 一、条件变量的概念
    • 二、条件变量的定义
    • 三、条件变量的使用
      • ① pthread_cond_init() 初始化
      • ② pthread_cond_destroy() 销毁
      • ③ 等待条件满足
      • ④ 唤醒等待
      • ⑤ 代码测试
    • 四、为什么 pthread_cond_init() 需要互斥量❓

在这里插入图片描述

Ⅰ. 线程同步概念

​ 还记得我们在学线程互斥的时候写的抢票系统代码吗,虽说我们通过使用互斥量来防止多线程访问时候的不安全问题,但是还有一个问题我们还没提及,就是如果当我们执行完临界区代码的时候,如果不进行 usleep() 的话,会发现执行结果全是单个线程在抢着票,而这就涉及到了线程同步与竞争条件问题!下面我们一一来了解一下它们!

  • 线程同步:在保证数据安全的前提下,让多个线程能够按照一定的顺序访问临界资源,从而有效 避免饥饿问题,叫做线程同步。
  • 竞态条件:因为时序问题,当多个线程访问同一个共享变量时,最终的结果取决于线程执行的顺序,导致结果不可预测。

​ 为了解决这些问题,可以使用同步机制来协调线程之间的访问。常用的同步机制有:

  1. 互斥锁(Mutex:在进入临界区前获取锁,执行完临界区代码后释放锁,保证同一时间只有一个线程可以访问临界区。
  2. 屏障(Barrier:多个线程需要等待其他线程执行完特定代码后再继续执行,可以使用屏障来同步线程的执行。
  3. 信号量(Semaphore:控制对共享资源的访问数量,当信号量为 0 时,线程需要等待;当信号量为 1 时,线程可以访问共享资源。
  4. 条件变量(Condition Variable):当某个条件不满足时,线程进入等待状态,直到其他线程发出特定信号或事件满足条件后,才会唤醒等待线程。

​ 之前我们通过了互斥锁和屏障(解锁完休眠了一会)在一定程度上解决了这个问题,下面我们来学习条件变量和信号量!

Ⅱ. 条件变量

一、条件变量的概念

条件变量(Condition Variable是一种同步机制,通常与互斥锁(Mutex)结合使用,用于线程间的等待和唤醒。它提供了一个让线程进入等待状态并释放锁的机制:

  • 当某个条件满足时,唤醒等待线程继续执行。
  • 当某个条件不满足时,线程进入等待状态,直到其他线程发出特定信号或事件满足条件后,才会唤醒等待线程。

​ 当一个线程互斥地访问某个变量时,它可能发现在其它线程改变状态之前,它什么也做不了,一直阻塞着。例如一个线程访问队列时,发现队列为空,它只能等待,只到其它线程将一个节点添加到队列中。这种情况就需要用到条件变量。

​ 条件变量可以分为两种类型:一种是用于通知单个线程的条件变量(signal,另一种是用于通知多个线程的条件变量(broadcast。使用条件变量的基本流程如下:

  1. 初始化:使用 pthread_cond_init() 函数初始化条件变量。
  2. 等待:使用 pthread_cond_wait() 函数让线程进入等待状态并释放互斥锁,直到其他线程调用条件变量的通知函数将其唤醒。
  3. 通知单个线程:使用 pthread_cond_signal() 函数唤醒等待队列中的一个线程,通常是等待时间最长的线程。
  4. 通知多个线程:使用 pthread_cond_broadcast() 函数唤醒等待队列中的所有线程。
  5. 销毁:使用 pthread_cond_destroy() 函数销毁条件变量。

​ 接下来我们先学学条件变量的这些接口,然后通过生产者消费者模型来深入理解条件变量的用处!

二、条件变量的定义

​ 定义条件变量对象的代码如下:

pthread_cond_t condition;

pthread_cond_t 数据类型声明如下:

// come from /usr/include/bits/pthreadtypes.h
/* Data structure for conditional variable handling. The structure of the attribute type is not exposed on purpose. */
typedef union
{struct{int __lock; // 用于实现对条件变量的互斥访问unsigned int __futex; // 一个底层的系统调用,用于实现线程等待和唤醒,它是一个用户空间和内核空间之间的交互接口,用于实现线程的同步和通信__extension__ unsigned long long int __total_seq;  // 记录条件变量被唤醒的次数,包括所有线程的唤醒次数__extension__ unsigned long long int __wakeup_seq; // 记录条件变量被唤醒的次数__extension__ unsigned long long int __woken_seq;  // 最后一次被唤醒的时间戳void *__mutex; // 与条件变量相关联的互斥锁的指针/地址unsigned int __nwaiters;     // 记录当前等待的线程数unsigned int __broadcast_seq; // 记录最后一次broadcast的时间戳} __data;char __size[__SIZEOF_PTHREAD_COND_T]; // 指定条件变量的大小__extension__ long long int __align; // 指定该类型的对齐方式
} pthread_cond_t;

三、条件变量的使用

① pthread_cond_init() 初始化

#include <pthread.h>
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);// 功能:动态初始化条件变量
// 返回值:成功返回0,失败返回错误编号
// 参数:
//     cond:线程要等待的目标条件变量
//     attr:条件变量属性,默认设为nullptr即可

​ 上面那种方法是通过动态初始化条件变量,其实我们还可以通过静态的方式来初始化条件变量,并且这种方式不用进行条件变量的销毁!

#include <pthread.h>
pthread_cond_t cond 

相关文章:

  • 逆向|dy|a_bogus|1.0.1.19-fix.01
  • RK3568 Debian调试记录
  • 基于强化学习的智能交通控制系统设计
  • 基于STM32单片机PWM讲解(HAL库)
  • html css js网页制作成品——HTML+CSS+js美甲店网页设计(5页)附源码
  • pytest 技术总结
  • Windows怎样使用curl下载文件
  • 大模型时代的语言格局演变:为什么是 JavaScript?
  • uml类关系(实现、继承,聚合、组合,依赖、关联)
  • Python并发编程全景解析:多线程、多进程与协程的深度对比
  • 职场十二法则-马方
  • 刚体运动 (位置向量 - 旋转矩阵) 笔记 1.1~1.3 (台大机器人学-林沛群)
  • Python Cookbook-6.11 缓存环的实现
  • 光子计算芯片进展评估:下一代AI算力突破的可能性
  • 逻辑运算符
  • C++之map
  • 缓存替换算法之 FIFO(先进先出)
  • L1-4 零头就抹了吧
  • 图解 Redis 事务 ACID特性 |源码解析|EXEC、WATCH、QUEUE
  • 第5讲:不同杂志风格主题复刻指南——打造像Nature、Science、Cell那样的高水准科研图表!
  • 公交公司须关注新出行需求:“单车巴士”能否常态化
  • 阿曼外交大臣:伊美下一轮谈判暂定5月3日举行
  • 习近平在中共中央政治局第二十次集体学习时强调,坚持自立自强,突出应用导向,推动人工智能健康有序发展
  • “归雁经济”能带来什么?川大商学院调研团队深入乡村与返乡青年人才交流
  • 中宣部版权管理局:微短剧出海面临版权交易不畅、海外维权较难等难题
  • 猿辅导武汉公司一员工猝死,死者亲属:他原计划5月2日举行婚礼