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;
};

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LabVIEW对EAST长脉冲等离子体运行的陀螺稳态运行控制

LabVIEW对EAST长脉冲等离子体运行的陀螺稳态运行控制 托卡马克是实现磁约束核聚变最有希望的解决方案之一。电子回旋共振加热&#xff08;ECRH是一种对托卡马克有吸引力的等离子体加热方法&#xff0c;具有耦合效率高&#xff0c;功率沉积定位好等优点。陀螺加速器是ECRH系统中…

JAVA设计模式第十讲:SPI - 业务差异解决方案

JAVA设计模式第十讲&#xff1a;SPI - 业务差异解决方案 我们需要在不修改源代码的情况下&#xff0c;动态为程序提供一系列额外的特性。首先想到的是Spring的AOP技术来构建应用插件&#xff0c;但是在Java自带的插件中&#xff0c;就有完整的实现。SPI&#xff08;Service Pro…

Win7旗舰版64位桌面创建32位IE方法

很多Win7 64位旗舰版用户系统桌面上的IE8浏览器&#xff0c;打开后都是64位的&#xff0c;而很多网站并不兼容64位的IE浏览器&#xff0c;其实在Win764位系统中IE是分为64位和32位的&#xff0c;出现这样的情况可能是桌面上的IE图标指响的是64位的IE&#xff0c;我们只要重新添…

grid弹性布局 设置宽高一致

效果图如下&#xff1a; 例子&#xff1a;设置每行四列的弹性布局&#xff0c;每个盒子宽高相同&#xff0c;间距为10px .left_list{display: grid;grid-gap: 10px 10px;grid-template-columns: repeat(4,1fr);.list_item{height: 0;padding-bottom:100%;/*高度设置为0&#…

io和进程day03(文件IO、文件属性函数、目录相关函数)

今日任务 代码 #include <stdio.h> #include <string.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <sys/types.h> #include <pwd.h> #include <dirent.h> #in…

【图文并茂】C++介绍之串

1.1串 引子—— ​ 字符串简称为串&#xff0c;串是由字符元素构成的&#xff0c;其中元素的逻辑关系也是一种线性关系。串的处理在计算机非数值处理中占用重要的地位&#xff0c;如信息检索系统&#xff0c;文字编辑等都是以串数据作为处理对象 串是由零个或多个字符组成的…

得心应手应对 OOM 的疑难杂症

Java全能学习面试指南&#xff1a;https://www.javaxiaobear.cn/ 前面我们提到&#xff0c;类的初始化发生在类加载阶段&#xff0c;那对象都有哪些创建方式呢&#xff1f;除了我们常用的 new&#xff0c;还有下面这些方式&#xff1a; 使用 Class 的 newInstance 方法。使用…

[Vue3 博物馆管理系统] 使用Vue3、Element-plus的Layout 布局构建组图文章

系列文章目录 第一章 定制上中下&#xff08;顶部菜单、底部区域、中间主区域显示&#xff09;三层结构首页 第二章 使用Vue3、Element-plus菜单组件构建菜单 第三章 使用Vue3、Element-plus走马灯组件构建轮播图 第四章 使用Vue3、Element-plus tabs组件构建选项卡功能 第五章…

PHP反序列化漏洞

一、序列化&#xff0c;反序列化 序列化&#xff1a;将php对象压缩并按照一定格式转换成字符串过程反序列化&#xff1a;从字符串转换回php对象的过程目的&#xff1a;为了方便php对象的传输和存储 seriallize() 传入参数为php对象&#xff0c;序列化成字符串 unseriali…

go gin gorm连接postgres postgis输出geojson

go gin gorm连接postgres postgis输出geojson 1. 技术环境 go-gin-gorm postgres-postgis go vscode环境安装-智能提示配置 2. 简单实现代码 思路就是&#xff1a;采用原生sql实现查询、更新等&#xff0c;采用gorm的raw来执行sql语句 package mainimport ("fmt"&q…

JavaScript 之 Symbol 数据类型

一、简介 ​ symbol类型是ES6新引入的一种基本数据类型&#xff0c;该类型具有静态属性和静态方法。其中静态属性暴露了几个内建的成员对象&#xff0c;静态方法暴露了全局的symbol注册。 ​ symbol类型具有以下特点&#xff1a;① 唯一性&#xff1a;每个symbol值都是唯一的…

JavaScript设计模式(四)——策略模式、代理模式、观察者模式

个人简介 &#x1f440;个人主页&#xff1a; 前端杂货铺 &#x1f64b;‍♂️学习方向&#xff1a; 主攻前端方向&#xff0c;正逐渐往全干发展 &#x1f4c3;个人状态&#xff1a; 研发工程师&#xff0c;现效力于中国工业软件事业 &#x1f680;人生格言&#xff1a; 积跬步…

【MySQL基础】事务隔离03

目录 隔离性与隔离级别事务隔离的实现事务的启动方式MySQL事务代码示例 在MySQL中&#xff0c;事务支持是在引擎层实现的。MySQL是一个支持多引擎的系统&#xff0c;但并不是所有的引擎都支持事务。比如 MySQL 原生的 MyISAM 引擎就不支持事务&#xff0c;这也是 MyISAM 被 Inn…

29 | 聊聊性能测试的基本方法与应用领域

并发用户数、响应时间、系统吞吐量之间的关系 当系统并发用户数较少时&#xff0c;系统的吞吐量也低&#xff0c;系统处于空闲状态&#xff0c;这个阶段被称为 “空闲区间”。 并发用户数进一步增长&#xff0c;系统的处理能力逐渐趋于饱和&#xff0c;因此每个用户的响应时间会…

Java 日志技术

所以说&#xff0c;要学Logback&#xff01; 配置文件 Logback提供了一个核心配置文件logback.xml&#xff0c;日志框架在记录日志时会读取配置文件中的配置信息&#xff0c;从而记录日志的形式。 可以配置日志输出的位置是文件还是控制台可以配置日志输出的格式还可以配置日…

55、基于 WebFlux 开发 WebSocKet

★ 基于Web Flux开发WebSocket 两步&#xff1a; &#xff08;1&#xff09;实现WebSocketHandler开发WebSocket处理类。 实现该接口时只需要实现Mono handle(WebSocketSession webSocketSession)方法即可。 &#xff08;2&#xff09;使用HandlerMapping和WebSocketHandler…

【Arduino27】DHT11温湿度传感器模拟值实验

硬件准备 DHT11温湿度&#xff1a;1个 面包板&#xff1a;1个 杜邦线&#xff1a;3根 硬件连线 VDD引脚接 5V 电源 DATE引脚接 4号 接口 GND引脚接 GND 接口 软件程序 #include<DHT.h>#define DHT11_pin 4 //温湿度传感器引脚DHT dht(DHT11_pin,DHT11);float tem…

常用echart图总结

柱状图 - category-work,grid直角坐标,legend,series-bar柱状图,tooltip提示框 - makeapie echarts社区图表可视化案例

华为OD机试 - 最多颜色的车辆 - 数据结构map(Java 2022Q4 100分)

目录 专栏导读一、题目描述二、输入描述三、输出描述四、解题思路1、核心思想2、题做多了&#xff0c;你就会发现&#xff0c;这道题属于送分题&#xff0c;为什么这样说&#xff1f;3、具体解题思路&#xff1a; 五、Java算法源码六、效果展示1、输入2、输出 华为OD机试 2023B…

C++ 模板

模板&#xff1a; 模板&#xff0c;即数据是灵魂&#xff0c;其余为肉身&#xff0c;正所谓有趣的灵魂万里挑一&#xff0c;所以想要模板变得完美&#xff0c;关键在于数据&#xff1b;其余不过是抄作业的框架。 模板函数&#xff1a; 模板函数可以自动推导出你传给他的数据类型…