[Linux][多线程][三][条件变量][生产者消费者模型][基于BlockingQueue的生产者消费者模型]详细讲解

目录

  • 1.线程同步
    • 1.同步概念与竞态条件
    • 2.条件变量
  • 2.条件变量函数
    • 1.初始化 -- pthread_cond_init()
    • 2.销毁 -- pthread_cond_destroy()
    • 3.等待条件变量 -- pthread_cond_wait()
    • 4.唤醒等待
    • 5.为什么pthread_cond_wait()需要互斥量?
    • 6.错误的程序设计
    • 7.条件变量使用规范
  • 3.生产者消费者模型
    • 1.基本概念
    • 2.模型特点
    • 3.思考问题
    • 4.模型优点
  • 4.基于BlockingQueue的生产者消费者模型
    • 1.基本概念
    • 2.注意点


1.线程同步

1.同步概念与竞态条件

  • 同步: 在保证数据安全的前提下,让线程能够按照某种特定的顺序访问临界资源,从而有效避免饥饿问题
  • 竞态条件: 因为时序问题,而导致程序异常
  • 单独使用互斥容易导致饥饿问题,为了解决此问题引入了同步:
    • 首先需要明确的是,单纯的加锁是会存在某些问题的,如果个别线程的竞争力特别强,每次都能够申请到锁,但申请到锁之后什么也不做
      • 在我们看来这个线程就一直在申请锁和释放锁
      • 这就可能导致其他线程长时间竞争不到锁,引起饥饿问题
    • 单纯的加锁是没有错的,它能够保证在同一时间只有一个线程进入临界区,但它没有高效的让每一个线程使用这份临界资源
    • 现在增加一个规则,当一个线程释放锁后,这个线程不能立马再次申请锁,该线程必须排到这个锁的资源等待队列的最后
    • 增加这个规则之后,下一个获取到锁的资源的线程就一定是在资源等待队列首部的线程
      • 如果有十个线程,此时就能够让这十个线程按照某种次序进行临界资源的访问

2.条件变量

  • 条件变量是利用线程间共享的全局变量进行同步的一种机制,条件变量是用来描述某种资源是否就绪的一种数据化描述
  • 条件变量主要包括两个动作
    • 一个线程等待条件变量的条件成立而被挂起
    • 另一个线程使条件成立后唤醒等待的线程
  • 条件变量通常需要配合互斥锁一起使用
  • 条件变量使唤醒线程由 系统唤醒 --> 让程序员自己唤醒线程

2.条件变量函数

1.初始化 – pthread_cond_init()

  • 静态分配:
    • pthread_cond_t cond = PTHREAD_COND_INITIALIZER
  • 动态分配:
    • 原型:int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
    • 参数:
      • **cond:**需要初始化的条件变量
      • **attr:**初始化条件变量的属性,一般设置为nullptr即可
    • **返回值:**成功返回0,失败返回错误码

2.销毁 – pthread_cond_destroy()

  • 原型:int pthread_cond_destroy(pthread_cond_t *cond);
  • 参数:
    • cond**:**需要销毁的条件变量
  • **返回值:**成功返回0,失败返回错误码
  • 注意:使用PTHREAD_COND_INITIALIZER初始化的条件变量不需要销毁

3.等待条件变量 – pthread_cond_wait()

  • 原型:int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
  • 参数:
    • cond**:**需要等待的条件变量
    • mutex**:**当前线程所处临界区对应的互斥锁
  • **返回值:**成功返回0,失败返回错误码
  • 注意:wait一定要在加锁和解锁之间进行wait!

4.唤醒等待

  • 原型:int pthread_cond_broadcast(pthread_cond_t *cond);
    • **功能:**唤醒等待队列中的全部线程
  • 原型:int pthread_cond_signal(pthread_cond_t *cond);
    • **功能:**唤醒等待队列中的首个线程
  • 参数:
    • **cond:**需要等待的条件变量
  • **返回值:**成功返回0,失败返回错误码

5.为什么pthread_cond_wait()需要互斥量?

  • 条件等待是线程间同步的一种手段,如果只有一个线程,条件不满足,一直等下去都不会满足
    • 所以必须要有一个线程通过某些操作,改变共享变量,使原先不满足的条件变得满足,并且友好的通知等待在条件变量上的线程
  • 条件不会无缘无故的突然变得满足,必然会牵扯到共享数据的变化
    • 所以一定要用互斥锁来保护
    • 没有互斥锁就无法安全的获取和修改共享数据
  • 当线程进入临界区时需要先加锁,然后判断内部资源的情况
    • 若不满足当前线程的执行条件,则需要在该条件变量下进行等待
    • 但此时该线程是拿着锁被挂起的,也就意味着这个锁再也不会被释放了,此时就会发生死锁问题
  • 所以在调用pthread_cond_wait函数时,还需要将对应的互斥锁传入
    • 此时当线程因为某些条件不满足需要在该条件变量下进行等待时,就会自动释放该互斥锁
  • 当该线程被唤醒时,该线程会接着执行临界区内的代码,此时便要求该线程必须立马获得对应的互斥锁
    • 因此当某一个线程被唤醒时,实际会自动获得对应的互斥锁
  • 总结:
    • 等待条件满足的时候往往是在临界区内等待的
      • 当该线程进入等待的时候,互斥锁会自动释放
      • 而当该线程被唤醒时,又会自动获得对应的互斥锁
    • 条件变量需要配合互斥锁使用,其中条件变量是用来完成同步的,而互斥锁是用来完成互斥的
    • pthread_cond_wait函数有两个功能,一就是让线程在特定的条件变量下等待,二就是让线程释放对应的互斥锁

6.错误的程序设计

pthread_mutex_lock(&mutex);
while (condition_is_false)
{pthread_mutex_unlock(&mutex);// 解锁之后,等待之前,条件可能已经满足,信号已经发出,但是该信号可能被错过pthread_cond_wait(&cond);pthread_mutex_lock(&mutex);
}
pthread_mutex_unlock(&mutex);
  • 由于解锁和等待不是原子操作。调用解锁之后, pthread_cond_wait之前,如果已经有其他线程获取到互斥量,摒弃条件满足,发送了信号
    • 那么pthread_cond_wait将错过这个信号,可能会导致线程永远阻塞在这个pthread_cond_wait
    • 所以解锁和等待必须是一个原子操作
  • 实际进入pthread_cond_wait()后,会先判断条件变量是否等于0
    • 等于0则说明不满足,此时会先将对应的互斥锁解锁 --> 互斥量变成1
    • 直到函数返回时再将条件变量改为1,并将对应的互斥锁加锁 --> 互斥量变为0

7.条件变量使用规范

  • 等待条件代码
    • 为什么使用while循环? --> 怕发生虚假唤醒
pthread_mutex_lock(&mutex);
while (条件为假)pthread_cond_wait(cond, mutex);
修改条件
pthread_mutex_unlock(&mutex);
  • 给条件发送信号代码
pthread_mutex_lock(&mutex);
设置条件为真
pthread_cond_signal(cond);
pthread_mutex_unlock(&mutex);

3.生产者消费者模型

1.基本概念

  • 生产者消费者模式就是通过一个容器(阻塞队列)解决生产者和消费者的强耦合问题
  • 生产者和消费者彼此之间不直接通讯,而通过这个容器来通讯
    • 所以生产者生产完数据之后不用等待消费者处理,直接将生产的数据放到这个容器当中
    • 消费者也不用找生产者要数据,而是直接从这个容器里取数据
    • 这个容器就相当于一个缓冲区,平衡了生产者和消费者的处理能力
    • 这个容器实际上就是用来给生产者和消费者解耦的
  • 让消费者和生产者协同工作,合适的时候可能一直运行
    • 生产者和消费者并不会因为要互相等待对方的结果而阻塞,相当于双方可以并发执行
      请添加图片描述

2.模型特点

  • 总结为321原则
    • 3种关系:
      • 生产者和生产者(互斥关系)
      • 消费者和消费者(互斥关系)
      • 生产者和消费者(互斥关系、同步关系)
    • 2****种角色: 生产者和消费者
    • 1****个场所: 通常指的是内存中的一段缓冲区
  • 注意:
    • 互斥关系保证的是数据的正确性
    • 同步关系是为了让多线程之间协同起来
  • 编写生产者消费者模型的时候,本质就是对这三个特点进行维护

3.思考问题

  • 生产者和生产者、消费者和消费者、生产者和消费者,它们之间为什么会存在互斥关系?

    • 介于生产者和消费者之间的容器可能会被多个执行流同时访问,因此我们需要将该临界资源用互斥锁保护起来
    • 其中,所有的生产者和消费者都会竞争式的申请锁,因此生产者和生产者、消费者和消费者、生产者和消费者之间都存在互斥关系
  • 生产者和消费者之间为什么存在同步关系?

    • 如果让生产者一直生产,那么当生产者生产的数据将容器塞满后,生产者再生产数据就会生产失败。
    • 反之,让消费者一直消费,那么当容器当中的数据被消费完后,消费者再进行消费就会消费失败。
    • 虽然这样不会造成任何数据不一致的问题,但是这样会引起另一方的饥饿问题,是非常低效的
      • 应该让生产者和消费者访问该容器时具有一定的顺序性,比如让生产者先生产,然后再让消费者进行消费
  • 为什么要有生产者消费者模型?

    • 本质是用代码进行解耦的过程

4.模型优点

  • 解耦
  • 支持并发
  • 支持忙闲不均 --> 哪边的线程忙可以多分配一些线程
  • 如何理解解耦?
    • 如果在主函数中调用某一函数,那么必须等该函数体执行完后才继续执行主函数的后续代码,因此函数调用本质上是一种紧耦合
    • 对应到生产者消费者模型中,函数传参实际上就是生产者生产的过程,而执行函数体实际上就是消费者消费的过程,但生产者只负责生产数据,消费者只负责消费数据,在消费者消费期间生产者可以同时进行生产,因此生产者消费者模型本质是一种松耦合

4.基于BlockingQueue的生产者消费者模型

1.基本概念

  • 在多线程编程中阻塞队列(Blocking Queue)是一种常用于实现生产者和消费者模型的数据结构
  • 其与普通的队列区别:
    • 当队列为空时,从队列获取元素的操作将会被阻塞,直到队列中被放入了元素
    • 当队列满时,往队列里存放元素的操作也会被阻塞,直到有元素被从队列中取出
      请添加图片描述

2.注意点

  • 判断是否满足生产消费条件时不能用if,而应该用while
    • pthread_cond_wait函数是让当前执行流进行等待的函数,是函数就意味着有可能调用失败,调用失败后该执行流就会继续往后执行,就会出现错误(没有数据还拿,没有空间还放)。
    • 在多消费者的情况下,当生产者生产了一个数据后如果使用pthread_cond_broadcast函数唤醒消费者,就会一次性唤醒多个消费者,但待消费的数据只有一个,此时其他消费者就被伪唤醒了。
    • 为了避免出现上述情况,就要让线程被唤醒后再次进行判断,确认是否真的满足生产消费条件
      • 因此这里必须要用while进行判断

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

2024 OceanBase 开发者大会:OceanBase 4.3正式发布,打造PB级实时分析数据库

4月20日,2024 OceanBase开发者大会盛大召开,吸引了50余位业界知名的数据库专家和爱好者,以及来自全国各地的近600名开发者齐聚一堂。他们围绕一体化、多模、TP与AP融合等前沿技术趋势展开深入讨论,分享场景探索的经验和最佳实践&a…

金融风控信用评分卡建模(Kaggle give me credit数据集)

1 数据预处理数据 数据来源于Kaggle的Give Me Some Credit,包括25万条个人财务情况的样本数据 1.1 导包读数据 import pandas as pd import numpy as np import matplotlib.pyplot as plt from sklearn.ensemble import RandomForestRegressor import seaborn as …

换脸插件升级导致SDWebUI无法启动cannot import name ‘Undefined‘ from ‘pydantic.fields‘

今天在一台新的机器环境装了SDWEBUI,都使用最新的版本,升级了下换脸的插件,于是乎启动崩溃了。错误如下 Launching Web UI with arguments: --listen --skip-torch-cuda-test --disable-nan-check --skip-version-check --skip-python-versi…

C++信息学奥赛 数据结构认识

数据结构 1.1数据结构分类 1.2基本数据类型 1.3数字编码 1.4字符编码 1.1数据结构分类 数据结构如同一副稳固而多样的框架。为数据的有序组织提供了蓝图,算法得以在此基础上生动起来。 常用的数据结构包括哪些 , , &…

Django中的事务

1 开启全局的事务 DATABASES {default: {ENGINE: django.db.backends.mysql, # 使用mysql数据库NAME: tracerbackend, # 要连接的数据库USER: root, # 链接数据库的用于名PASSWORD: 123456, # 链接数据库的用于名HOST: 192.168.1.200, # mysql服务监听的ipPORT: 3306, …

2024新算法爱情进化算法(LEA)和经典灰狼优化器(GWO)进行无人机三维路径规划设计实验

简介: 2024新算法爱情进化算法(LEA)和经典灰狼优化器(GWO)进行无人机三维路径规划设计实验。 无人机三维路径规划的重要意义在于确保飞行安全、优化飞行路线以节省时间和能源消耗,并使无人机能够适应复杂环…

薄板样条插值TPS原理以及torch和opencv实现

薄板样条插值TPS原理以及torch和opencv实现 1、薄板样条插值TPS原理概述原理以及公式推导2、torch实现3、opencv实现1、薄板样条插值TPS原理 概述 薄板样条(Thin Plate Spline),简称TPS,是一种插值方法,可找到通过所有给定点的“最小弯曲”光滑曲面。因为它一般都是基于…

跟着野火从零开始手搓emWin(1)初识emWin

PS:在嵌入式领域,本人认为QT的应用范围和性能几乎吊打市面上所有的GUI工具。但是本人之所以学习emWin,是因为自己有些微不足道的小想法,需要通过它来实现。但是QT有点吃硬件的配置,为了MCU专门发行的QT我又懒得去弄&am…

vscode将本地服务转发到外网地址访问

示例中将本地的5500端口,用vscode进行端口转发,在外网地址访问服务 要转发的端口 转发端口 点击转发端口 输入要转发的端口,按下回车 Enter 点击允许,弹出确认界面后点击打开 转发端口已经成功配置上,右键可见性…

四川赢涟电子商务有限公司是做什么的?

在当今数字化浪潮中,电子商务以其独特的魅力和无限潜力,成为了商业领域的新宠。而在这股潮流中,四川赢涟电子商务有限公司以其对抖音电商的深入研究和专业服务,成为了行业内的佼佼者。 一、深耕抖音,领跑电商新赛道 四…

centos 安装配置文件中心 nacos2.2.3 稳定版

安装mysql 8 参考文章 centos7搭建mysql5.6 && mysql 8.0_centos7 mysql5.6-CSDN博客 安装 jdk 17 官网下载 对应的版本 Java Downloads | Oracle wget https://download.java.net/java/GA/jdk17.0.2/dfd4a8d0985749f896bed50d7138ee7f/8/GPL/openjdk-17.0.2_l…

(5)步态识别论文研读——GaitDAN:基于对抗域适应的跨视角步态识别

GaitDAN: Cross-view Gait Recognition via Adversarial Domain Adaptation | IEEE Journals & Magazine | IEEE Xplore GaitDAN: Cross-view Gait Recognition via Adversarial Domain Adaptation 基于对抗与适应 摘要:视角变化导致步态外观存在显着差异。因…

qt实现打包

qt实现打包 qt打包流程如下:打包你要注意的点教程 qt打包流程如下: 1,.Release编译: 1),找到release目录(一般会有debug和release两种模式),就是有exe目录的那个文件夹 2),给项目…

QT中基于TCP的网络通信

QT中基于TCP的网络通信 QTcpServer公共成员函数信号 QTcpSocket公共成员函数信号 通信流程服务器端通信流程代码 客户端通信流程代码 使用Qt提供的类进行基于TCP的套接字通信需要用到两个类: QTcpServer:服务器类,用于监听客户端连接以及和客…

牛客网刷题 | BC61 牛牛的二三七整除

描述 牛牛从键盘输入一个整数,请你判断这个整数能被 2 3 7 中哪几个数整除,并按升序输出。如果不能被 2 3 7 任意一个数整除则输出 n。 输入描述: 输入一个整数 输出描述: 输出能被 2 3 7 哪几个数整除,并按升序输…

华为海思校园招聘-芯片-数字 IC 方向 题目分享——第七套

华为海思校园招聘-芯片-数字 IC 方向 题目分享——第七套 (共9套,有答案和解析,答案非官方,未仔细校正,仅供参考) 部分题目分享,完整版获取(WX:didadidadidida313,加我备注&#x…

【软考经验分享】软考-中级-嵌入式备考

这里写目录标题 教辅用书嵌入式系统设计师考试大纲嵌入式系统设计师教程嵌入式系统设计师5天修炼嵌入式系统设计师考前冲刺100题 刷题软件希赛网软考真题 视频教程希赛网王道-计组计网 教辅用书 嵌入式系统设计师考试大纲 50页左右,内容为罗列一些考点&#xff0c…

QML 不同风格和主题的切换

Quick程序提供了方便的用于切换不同风格和主题的配置文件,如果没有设计稿,又想界面没那么丑,那么可以用这套配置,让应用看起来相对专业一点。 一,在 qrc 资源文件中添加 qtquickcontrols2.conf 文件。 二,…

YOLOv8改进项目汇总-超全改进-ultralyticsPro介绍:订阅了《芒果YOLOv8原创改进专栏》的读者免费赠送,包括很多稀有改进

🔥🔥🔥专注于YOLOv8改进,NEW - YOLOv8 🚀 in PyTorch >, Support to improve Backbone, Neck, Head, Loss, IoU, LA, NMS and other modules🚀 Makes YOLOv8 improvements easy again 芒果出品 YOLOv8…

AI计算中的光学模块:波分复用器的应用前景

在人工智能(AI)的计算领域,光学模块扮演着至关重要的角色。随着AI技术的飞速发展,对数据处理速度和带宽的需求日益增长。光学模块,特别是波分复用器(WDM),因其高速、大容量的数据传输…