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

Linux入门之多线程|线程的同步|生产消费模型

文章目录

一、多线程的同步

1.概念

2.条件变量

2.1条件变量概念

2.2条件变量接口

1.条件变量初始化

2.等待条件满足

3.唤醒等待

3.销毁条件变量

2.3条件变量demo

二、生产消费模型

1.生产消费模型

2.基于BlockQueue的生产者消费者模型

3.基于C++用条件变量和互斥锁实现一个生产消费模型

4.信号量

1.信号量概念

2.信号量接口

1.初始化信号量

2.等待信号量(P操作 --)

3.发布信号量(V操作 ++)

4.销毁信号量

5.环形生产者消费者模型



当一个线程互斥地访问某个变量时,它发现可能再其他线程改变状态之前,它被挂起。

例如一个线程访问队列,发现队列为空,它只能等待。直到其他线程将一个节点加入到队列中,这种情况就需要用到条件变量。

一、多线程的同步

1.概念

在保证数据安全的前提下,让线程能够按照某种特定的顺序访问临界资源从而有效避免饥饿问题,叫做同步。

2.条件变量

2.1条件变量概念

条件变量是线程同步的一种手段,如果只有一个线程,条件不满足,一直等待下去都不会满足,所以必须要有一个线程通过某些操作,改变共享变量,使得原来的不满足条件变的满足,并且友好通知等待在条件变量上的线程。

条件变量不会无缘无故满足,必然牵扯到共享数据的变化,所以一定需要用锁来保护,没有锁就无法安全的获取和修改共享数据

2.2条件变量接口

1.条件变量初始化

int pthread_cond_init(pthread_cond_t * restrict cond,const pthread_condattr_t * restrict attr);参数:cond 要初始化的条件变量attr:NULL

2.等待条件满足

int pthread_cond_wait(pthread_cond_t * restrict cond,pthread_mutex_t * restrict mutex);参数:cond: 要在这个条件变量上等待mutex:互斥量,等待的时候要释放掉这个锁

3.唤醒等待

int pthread_cond_broadcast(pthread_cond_t * cond);
int pthread_cond_signal(pthread_cond_t * cond);

3.销毁条件变量

int pthread_cond_destroy(pthread_cond_t * cond);

2.3条件变量demo

#include<iostream>
#include<pthread.h>
#include<unistd.h>
#include<cstdio>
#include<string>
using namespace std;const int num = 5;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;void *active(void *args)
{string name = static_cast<const char*>(args);while(true){pthread_mutex_lock(&mutex);pthread_cond_wait(&cond,&mutex);// 在调用的时候,会自动释放锁cout<<name<<" activing..."<<endl;pthread_mutex_unlock(&mutex);}}
int main()
{pthread_t tids[num];for(int i = 0; i<num;i++){char * name = new char[32];snprintf(name,32,"thread-%d",i+1);pthread_create(tids+i,nullptr,active,name);}sleep(3);while(true){cout<<"main thread wakeup thread"<<endl;pthread_cond_signal(&cond);sleep(1);}for(int i = 0; i<num;i++){pthread_join(tids[i],nullptr);}}

二、生产消费模型

1.生产消费模型

生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。这个阻塞队列就是用来给生产者和消费者解耦的。

2.基于BlockQueue的生产者消费者模型

在多线程编程中阻塞队列 (Blocking Queue) 是一种常用于实现生产者和消费者模型的数据结构。其与普通的队列区别在于,当队列为空时,从队列获取元素的操作将会被阻塞,直到队列中被放入了元素;当队列满时,往队列里存放元 素的操作也会被阻塞,直到有元素被从队列中取出(以上的操作都是基于不同的线程来说的,线程在对阻塞队列进程 操作时会被阻塞。

3.基于C++用条件变量和互斥锁实现一个生产消费模型

需求:使用条件变量和互斥锁实现一个生产消费模型。生产消费模型是一个队列,如上图,这里使用stl中的队列,先实现单生产单消费,一个线程负责生产,一个线程负责消费。这两个线程需要访问同一个队列,所以需要一把锁。在线程挂起的时候,还需要有信号告诉线程现在条件满足了,所以要使用两个条件变量,分别通知生产和消费线程转为就绪态。

实现:将这个模型封装成一个类,两个线程去访问的时候,队列非满,就可以生产,往队列里push数据,队列中数据非空,就可以消费,从队列里pop数据。所以需要两个接口push和pop,调试成功后,最后使用多生产多消费实现。实现代码如下:

//blockqueue.hpp 声明,方法,定义在一个文件中const int gcap = 5;
template<class T>
class blockQueue
{public://构造blockQueue(const int cap = gcap):_cap(cap),{pthread_mutex_init(&_mutex,nullptr);pthread_cond_init(&_consumerCond,nullptr);pthread_cond_init(&_productorCond,nullptr);}bool isFull() {return _q.size() == _cap;}bool isEmpty() { return _q.empty()};//将数据塞进队列 生产void push(const T & in){pthread_mutex_lock(&_mutex);//注意这里不要用if,可能会误唤醒while(isFull()){//在当前的条件下休眠,就注定了要释放锁,让别的线程去竞争锁//休眠,就是被os切走了,醒来之后又要重新申请锁pthread_cond_wait(&_productorCond,&_mutex);  }//如果没满,就让他继续生产_q.push(in);//生产之后,让消费者来消费,唤醒消费的线程 再释放自己手中的锁pthread_cond_signal(&_consumerCond);pthread_mutex_unlock(&_mutex);}//队列非空 消费  void pop(){pthread_mutex_lock(&_mutex);//判断队列是否为空while(isEmpty()){//空的话,在当前条件下休眠pthread_cond_wait(&_consumerCond,&_mutex);}//非空 开始消费 并且唤醒生产者 可以生产了pthread_cond_signal(&_productorCond);pthread_mutex_unlock(&_mutex);}//析构~blockQueue(){//释放锁和两个信号量 队列是一个临时变量可以不用在这里释放pthread_mutex_destroy(&mutex);pthread_cond_destroy(&_consumerCond);pthread_cond_destroy(&_productorCond);}private:std::queue<T> _q;int _cap; //队列中的容量pthread_mutex_t _mutex;pthread_cond_t _consumerCond; //消费者对应的条件变量,如果队列中数据为空,waitpthread_cond_t _productorCond; //生产者对应的条件变量,如果队列中数据为满,wait
};

4.信号量

1.信号量概念

        POSIX信号量和System V 信号量作用相同,都是用于同步操作,达到无冲突访问共享资源的目的。但是POSIX可以用于线程间同步。信号量本质就是用来描述临界资源中的数量

        sem = 1,就只有0/1两种状态,就是互斥锁。

        多元信号量:每个线程,在访问对应资源时,先申请信号量。申请成功,就表示现在可以时候该资源。申请失败,就目前无法访问。

2.信号量接口

#include<semaphore.h>

1.初始化信号量

int sem_init(sem_t * sem, int pshared ,unsiged int value);参数:pshared 0 表示线程间共享,非0 表示进程间共享value:信号量初始值

2.等待信号量(P操作 --)

int sem_wait(sem_t * sem);

3.发布信号量(V操作 ++)

int sem_post(sem_t * sem);

4.销毁信号量

int sem_destroy(sem_t * sem);

5.环形生产者消费者模型 

       环形队列采用数组模拟,用%运算来模拟环状特性。在为空/满的时候要保证游戏规则,在非空非满的时候保证并发。

环形结构起始状态和结束状态都是一样的,不容易判断是空还是满,所以通过加计数器或者标记位来判断。另外也可以预留一个空的位置,作为满的状态。

但是我们现在有信号量这个计数器,就可以进行多线程间的同步过程。

生产者关心这个空间是否满了,消费者关心是否有数据。环形队列只要访问不同的区域,生产和消费行为可以同时进行。

需求:生产消费模型是一个队列,使用数组模拟,同时需要两个线程,生产线程和消费线程。要维护3种关系:生产者和生产者,消费者和消费者,生产者和消费者之间的关系。其中生产者和生产者,需要互斥。消费者和消费者同样互斥。生产者和消费者,需要先生产再消费,所以需要同步。同时,访问同一个队列(共享资源)需要互斥关系。

实现:将队列封装成类,用数组模拟实现。类需要暴露的接口就是pushpop,即实现p操作和v操作。同时要知道这个队列的大小定义两个信号量,一个是消费者关心的,一个是生产者关心。申请信号量成功之后,也要知道生产和消费此刻对应队列中的位置,就是具体维护哪个区域。即两个下标。申请自己关心的资源,互相V对方的资源

static const int N = 5;template<class T>
class RingQueue
{private:void P(sem_t &s){sem_wait(&s);}void V(sem_t &s){sem_post(&s);}public://构造RingQueue(int num = N):_ring(num),_cap(num){sem_init(&_data_sem,0,0);        sem_init(&_space_sem,0,num);//刚开始都为0_c_step = _p_step = 0;}void push(const T &in){//申请P(_space_sem);_ring[_p_step++] = in;_p_step %= _cap;V(_data_sem);}void pop(T * out){P(_data_sem);*out = _ring[_c_step++];_c_step &= _cap;V(_spcae_sem);}~RingQueue(){sem_destroy(&_data_sem);sem_destroy(&_space_sem);}private:std::vector<T> _ring;int _cap;sem_t _data_sem;sem_t _space_sem;int _c_step;int _p_step;
};

相关文章:

Linux入门之多线程|线程的同步|生产消费模型

文章目录 一、多线程的同步 1.概念 2.条件变量 2.1条件变量概念 2.2条件变量接口 1.条件变量初始化 2.等待条件满足 3.唤醒等待 3.销毁条件变量 2.3条件变量demo 二、生产消费模型 1.生产消费模型 2.基于BlockQueue的生产者消费者模型 3.基于C用条件变量和互斥锁实…...

MATLAB解析和保存ini文件

1. 将ini文件转换成struct结构体 function data ini2struct(filename)fid fopen(filename, r);if fid -1error(Unable to open file %s., filename);enddata struct();section ;while ~feof(fid)line fgetl(fid);line strtrim(line);% 如果是注释行或者空行&#xff0c…...

模型压缩-对模型结构进行优化

模型压缩-对模型结构进行优化 概述 模型压缩通常都是对推断过程而言&#xff0c;训练过程的计算代价通常不考虑&#xff0c;因为GPU可以快速完成任意复杂度模型的训练对于推断过程来说&#xff0c;模型应用才是对于速度敏感的场景多数情况下 希望使用尽可能少的能耗完成京可能…...

软件工程课件

软件工程 考点概述软件工程概述能力成度模型能力成熟度模型集成软件过程模型逆向工程![ ](https://img-blog.csdnimg.cn/425cea8190fb4c5ab2bf7be5e2ad990e.png) 考点概述 重点章节 软件工程概述 之前老版教程的&#xff0c;之前考过 能力成度模型 记忆 能力等级 和 特点 能力…...

基于ADS的marx雪崩电路设计-设计实践(射频脉冲源)

基于ADS的marx雪崩电路设计-设计实践&#xff08;射频脉冲源&#xff09; 设计一个ns级别的脉冲源&#xff0c;属于是半路转行的&#xff0c;虽然不了解具体原理但是也可以进行设计。具体的设计理论以及优化方法将在之后进行讨论. 参考文献&#xff1a;基于Marx电路的亚纳秒级…...

X86_64函数调用汇编程序分析

X86_64函数调用汇编程序分析 1 X86_64寄存器使用标准2 对应代码的分析2.1 main函数及其对应的汇编程序2.1.1 main的C代码实现2.1.2 main函数对应汇编及其分析2.1.3 执行完成之后栈的存放情况 2.2 test_fun_a函数及其对应的汇编程序2.2.1 test_fun_a函数的C实现2.2.2 test_fun_a…...

Vue3【Provide/Inject】

前言 自从使用了Provide/Inject代码的组织方式更加灵活了&#xff0c;但是这个灵活性的增加伴随着代码容错性的降低。我相信只要是真的在项目中引入Provide/Inject的同学&#xff0c;一定一定有过或者正在经历下面的状况&#xff1a; 注入名&#xff08;Injection key&#x…...

Go-Python-Java-C-LeetCode高分解法-第四周合集

前言 本题解Go语言部分基于 LeetCode-Go 其他部分基于本人实践学习 个人题解GitHub连接&#xff1a;LeetCode-Go-Python-Java-C Go-Python-Java-C-LeetCode高分解法-第一周合集 Go-Python-Java-C-LeetCode高分解法-第二周合集 Go-Python-Java-C-LeetCode高分解法-第三周合集 本…...

vue路由

一、声明式导航-导航链接 1.需求 实现导航高亮效果 如果使用a标签进行跳转的话&#xff0c;需要给当前跳转的导航加样式&#xff0c;同时要移除上一个a标签的样式&#xff0c;太麻烦&#xff01;&#xff01;&#xff01; 2.解决方案 vue-router 提供了一个全局组件 router…...

最强的AI视频去码图片修复模型:CodeFormer

目录 1 CodeFormer介绍 1.1 CodeFormer解决的问题 1.2 人脸复原的挑战 1.3 方法动机 1.4 模型实现 1.5 实验结果 2 CodeFormer部署与运行 2.1 conda环境安装 2.2 运行环境构建 2.3 模型下载 2.4 运行 2.4.1 人脸复原 ​编辑​编辑 2.4.2 全图片增强 2.4.3 人脸颜色…...

jenkins自动化部署安装

一、准备工作 1、安装jdk # 1、下载准备jdk包(也可以用docker安装) wget ... # 2、直接解压到,无需安装 unzip ...2、安装maven # 1、下载准备maven压缩包 wget ... # 2、直接解压,无需安装 unzip ... # 3、修改setting.xml&#xff0c;修改localRepository和MIRROR镜像地址…...

如何调用Zabbix API获取主机信息

自Zabbix 1.8版本被引进以后&#xff0c;Zabbix API开始扮演着越来越重要的角色&#xff0c;它可以为批量操作、第三方软件集成以及其他应用提供可编程接口。 在运维实践中&#xff0c;Zabbix API还有更多巧妙的应用。 面对规模庞大的监控设备&#xff0c;可能会出现某台机器发…...

批量执行redis命令总结

目录 批量执行redis命令方式1: redis-cli直接执行方式2:通过redis-cli和xargs等命令 批量执行redis命令 方式1: redis-cli直接执行 redis-cli command param redis-cli本身支持单个命令执行省略了连接参数操作的key等相关数据&#xff0c;可以通过线下获取或通过keys scan等命…...

命令行git联网失败,但是实际可以联网

最近下载代码的时候发现总是告诉我连不上github的网页&#xff0c;但是我自己通过浏览器又可以上网&#xff0c;找了半天发现这个方法可以。 记录下这个代理 打开git bash 执行以下命令&#xff1a; git config --global http.proxy http://127.0.0.1:7890 git config --glob…...

网络编程套接字,Linux下实现echo服务器和客户端

目录 1、一些网络中的名词 1.1 IP地址 1.2 端口号port 1.3 "端口号" 和 "进程ID" 1.4 初始TCP协议 1.5 UDP协议 2、socket编程接口 2.1 socket 常见API 2.2 sockaddr结构 3、简单的网络程序 3.1 udp实现echo服务器和客户端 3.1.1 echo服务器实…...

java+ssh+mysql智能化办公管理系统

项目介绍&#xff1a; 本系统为基于jspsshmysql的OA智能办公管理系统&#xff0c;包含管理员、领导、员工角色&#xff0c;功能如下&#xff1a; 管理员&#xff1a;公告信息&#xff1b;工作计划&#xff1b;公司资料&#xff1b;部门管理&#xff1b;员工管理&#xff1b;员…...

网络层抓包tcpdump

sudo tcpdump -i eth0 -s 0 -nn host iphost -w xxx.pcap 这段代码使用了命令行工具 tcpdump&#xff0c;用于在Linux系统上捕获网络数据包。让我详细介绍一下这段代码的含义和 tcpdump 的用法&#xff1a; 代码含义&#xff1a; sudo: 使用超级用户权限执行 tcpdump 命令&am…...

QT之形态学操作

形态学操作包含以下操作&#xff1a; 腐蚀 (Erosion)膨胀 (Dilation)开运算 (Opening)闭运算 (Closing)形态梯度 (Morphological Gradient)顶帽 (Top Hat)黑帽(Black Hat) 其中腐蚀和膨胀操作是最基本的操作&#xff0c;其他操作由这两个操作变换而来。 腐蚀 用一个结构元素…...

15、监测数据采集物联网应用开发步骤(11)

源码将于最后一遍文章给出下载 监测数据采集物联网应用开发步骤(10) 程序自动更新开发 前面章节写了部分功能模块开发&#xff1a; 日志或文本文件读写开发;Sqlite3数据库读写操作开发;定时器插件化开发;串口(COM)通讯开发;TCP/IP Client开发;TCP/IP Server 开发;modbus协议…...

Pygame中Trivia游戏解析6-2

3.1.2 读取保存题目的文件 在Trivia类的__init__()方法中&#xff0c;对各变量初始化完成之后&#xff0c;读取保存题目的文件&#xff0c;代码如下所示。 f open(filename, "r", encodingutf8) trivia_data f.readlines() f.close() 其中&#xff0c;open()函数…...

Unity3D中Gfx.WaitForPresent优化方案

前言 在Unity中&#xff0c;Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染&#xff08;即CPU被阻塞&#xff09;&#xff0c;这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案&#xff1a; 对惹&#xff0c;这里有一个游戏开发交流小组&…...

day52 ResNet18 CBAM

在深度学习的旅程中&#xff0c;我们不断探索如何提升模型的性能。今天&#xff0c;我将分享我在 ResNet18 模型中插入 CBAM&#xff08;Convolutional Block Attention Module&#xff09;模块&#xff0c;并采用分阶段微调策略的实践过程。通过这个过程&#xff0c;我不仅提升…...

通过Wrangler CLI在worker中创建数据库和表

官方使用文档&#xff1a;Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后&#xff0c;会在本地和远程创建数据库&#xff1a; npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库&#xff1a; 现在&#xff0c;您的Cloudfla…...

LeetCode - 394. 字符串解码

题目 394. 字符串解码 - 力扣&#xff08;LeetCode&#xff09; 思路 使用两个栈&#xff1a;一个存储重复次数&#xff0c;一个存储字符串 遍历输入字符串&#xff1a; 数字处理&#xff1a;遇到数字时&#xff0c;累积计算重复次数左括号处理&#xff1a;保存当前状态&a…...

全球首个30米分辨率湿地数据集(2000—2022)

数据简介 今天我们分享的数据是全球30米分辨率湿地数据集&#xff0c;包含8种湿地亚类&#xff0c;该数据以0.5X0.5的瓦片存储&#xff0c;我们整理了所有属于中国的瓦片名称与其对应省份&#xff0c;方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...

Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具

文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...

华硕a豆14 Air香氛版,美学与科技的馨香融合

在快节奏的现代生活中&#xff0c;我们渴望一个能激发创想、愉悦感官的工作与生活伙伴&#xff0c;它不仅是冰冷的科技工具&#xff0c;更能触动我们内心深处的细腻情感。正是在这样的期许下&#xff0c;华硕a豆14 Air香氛版翩然而至&#xff0c;它以一种前所未有的方式&#x…...

20个超级好用的 CSS 动画库

分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码&#xff0c;而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库&#xff0c;可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画&#xff0c;可以包含在你的网页或应用项目中。 3.An…...

【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制

使用 NginxLua 实现基于 IP 的访问频率限制 在高并发场景下&#xff0c;限制某个 IP 的访问频率是非常重要的&#xff0c;可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案&#xff0c;使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制…...

基于Springboot+Vue的办公管理系统

角色&#xff1a; 管理员、员工 技术&#xff1a; 后端: SpringBoot, Vue2, MySQL, Mybatis-Plus 前端: Vue2, Element-UI, Axios, Echarts, Vue-Router 核心功能&#xff1a; 该办公管理系统是一个综合性的企业内部管理平台&#xff0c;旨在提升企业运营效率和员工管理水…...