【Linux】线程——生产者消费者模型、基于阻塞队列的生产消费者模型、基于环形队列的生产消费者模型、POSIX信号量的概念和使用

文章目录

  • Linux线程
    • 6. 生产消费者模型
      • 6.1 基于阻塞队列的生产消费者模型
        • 6.1.1 阻塞队列模型实现
      • 6.2 基于环形队列的生产消费者模型
        • 6.2.1 POSIX信号量的概念
        • 6.2.2 POSIX信号量的使用
        • 6.2.3 环形队列模型实现

Linux线程

6. 生产消费者模型

生产消费者模型的概念

  生产者消费者模型 是一种常见的并发编程模型。

  在这个模型中,通常存在两类角色:生产者和消费者。

  生产者负责生成数据或产品,并将其放入一个共享的缓冲区中。而消费者则从缓冲区中取出数据或产品进行消费处理。

  

为何要使用生产者消费者模型

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

  

  简单的说:就是为了解决生产者和消费者之间的速度不匹配问题。

  

生产者消费者模型优点

  解耦、支持并发、支持忙闲不均。

在这里插入图片描述
  

  上图中有"321"原则:

  3种关系:生产者和生产者(互斥)、消费者和消费者(互斥)、生产者和消费者(互斥、同步)。

  2种角色:生产者和消费者。

  1个交易场所:特定结构的内存空间。

  

6.1 基于阻塞队列的生产消费者模型

基于阻塞队列的生产消费者模型的概念

  基于阻塞队列的生产消费者模型 是一种用于协调生产者和消费者之间工作流程的编程模式。

  

实现的原理

  在这个模型中,生产者负责生成数据或产品,并将其放入一个阻塞队列中。阻塞队列是一种特殊的数据结构,当队列已满时,生产者尝试添加新元素的操作会被阻塞,直到队列有足够的空间。

  消费者则从这个阻塞队列中获取数据或产品进行处理。当队列为空时,消费者尝试获取元素的操作会被阻塞,直到生产者向队列中添加了新的元素。

  

在这里插入图片描述

  

6.1.1 阻塞队列模型实现

  这是我们的任务对象,一个简单的模拟加减乘除计算:

#pragma once
#include <iostream>
#include <string>std::string opers="+-*/%";enum{DivZero=1,ModZero,Unknown
};class Task
{
public:Task(){}Task(int x,int y,char op):_data1(x),_data2(y),_oper(op),_result(0),_exitcode(0){}void run(){switch (_oper){case '+':_result=_data1+_data2;break;case '-':_result=_data1-_data2;break;case '*':_result=_data1*_data2;break;case '/':{if(_data2==0) _exitcode=DivZero;else _result=_data1/_data2;}break;case '%':{if(_data2==0) _exitcode=ModZero;else _result=_data1%_data2;}break;default:_exitcode=Unknown;break;}}//Task对象重载运算符(),()直接进行run函数void operator()(){run();}std::string GetResult(){std::string r=std::to_string(_data1);r+=_oper;r+=std::to_string(_data2);r+="=";r+=std::to_string(_result);r+="[code: ";r+=std::to_string(_exitcode);r+="]";return r;}std::string GetTask(){std::string r=std::to_string(_data1);r+=_oper;r+=std::to_string(_data2);r+="=?";return r;}~Task(){}private: int _data1;int _data2;char _oper;int _result;int _exitcode;
};

  

  这是我们实现的阻塞队列模型:

#pragma once#include <iostream>
#include <queue>
#include <unistd.h>template<class T>
class blockQueue
{static const int defaultnum=5;public://构造函数blockQueue(int maxp=defaultnum):_maxp(maxp){pthread_mutex_init(&_mutex,nullptr);pthread_cond_init(&_p_cond,nullptr);pthread_cond_init(&_c_cond,nullptr);}T pop(){pthread_mutex_lock(&_mutex);if(_q.size()==0){pthread_cond_wait(&_c_cond,&_mutex);}T out=_q.front();_q.pop();pthread_cond_signal(&_p_cond);pthread_mutex_unlock(&_mutex);return out;}void push(const T &in){pthread_mutex_lock(&_mutex);if(_q.size()==_maxp){pthread_cond_wait(&_p_cond,&_mutex);}_q.push(in);pthread_cond_signal(&_c_cond);pthread_mutex_unlock(&_mutex);}//析构函数~blockQueue(){pthread_mutex_destroy(&_mutex);pthread_cond_destroy(&_p_cond);pthread_cond_destroy(&_c_cond);}private:std::queue<T> _q; //生产消费队列int _maxp; //最大值pthread_mutex_t _mutex; //锁pthread_cond_t _p_cond; //生产同步队列pthread_cond_t _c_cond; //消费同步队列
};

  

  运行函数:

#include "blockQueue.hpp"
#include "Task.hpp"
#include <unistd.h>
#include <ctime>//消费者模型
void *Consumer(void *args)
{blockQueue<Task> *bp=static_cast<blockQueue<Task>*>(args);while(true){Task t=bp->pop();t();std::cout<<"thread id: "<<pthread_self()<<" 处理了一个任务:"<<t.GetTask()\<<" 处理后的任务结果:"<<t.GetResult()<<std::endl;sleep(3);}
}//生产者模型
void *Productor(void *args)
{blockQueue<Task> *bp=static_cast<blockQueue<Task>*>(args);int len=opers.size();int data;while(true){int data1=rand()%10+1;usleep(10);int data2=rand()%10;char op=opers[rand()%len];Task t(data1,data2,op);bp->push(t);sleep(1);std::cout<<"thread id: "<<pthread_self()<<" 生产了一个任务:"<<t.GetTask()<<std::endl;sleep(2);}
}int main()
{srand(time(nullptr));blockQueue<Task> *bp=new blockQueue<Task>();pthread_t c[3],p[5];for(int i=0;i<5;i++){pthread_create(p+i,nullptr,Productor,bp);}for(int i=0;i<3;i++){  pthread_create(c+i,nullptr,Consumer,bp);}for(int i=0;i<5;i++){pthread_join(p[i],nullptr);}for(int i=0;i<3;i++){pthread_join(c[i],nullptr);}delete bp;return 0;
}

  

  基于阻塞队列的生产消费者的模型实现了(就是打出来乱乱的):

在这里插入图片描述

  

6.2 基于环形队列的生产消费者模型

基于环形队列的生产消费者模型的概念

  基于环形队列的生产消费者模型是一种常见的并发编程模型,其中使用环形队列作为生产者和消费者之间共享的数据结构。

  

实现的原理

  环形队列是一种特殊的数据结构,它的特点是队列的尾部与头部相连,形成一个环状。在基于环形队列的生产消费者模型中,生产者负责向队列中添加数据,消费者负责从队列中取出数据。

  为了保证生产者和消费者之间的正确协作,需要遵循以下三个原则:

  生产者不能超过消费者:也就是说,生产者生产数据的速度不能超过消费者处理数据的速度,否则队列会溢出。

  生产者不能领先消费者一圈:也就是说,生产者和消费者之间不能有太大的差距,否则会导致数据丢失或重复。

  在同一位置时,生产者和消费者必须互斥:也就是说,当生产者和消费者在同一位置时,只能有一个进行操作,否则会导致数据不一致。

  

在这里插入图片描述

  

6.2.1 POSIX信号量的概念

  POSIX信号量和SystemV信号量作用相同,都是用于同步操作,达到无冲突的访问共享资源目的。 但POSIX可以用于线程间同步。

  

实现的原理

  信号量本质上是一个整数,其值表示资源的可用数量。 通过 sem_wait 操作(也称为 P 操作)来请求资源,如果信号量的值大于 0 ,则减 1 并继续执行;如果值为 0 ,则阻塞当前线程或进程,直到信号量的值大于 0 。通过 sem_post 操作(也称为 V 操作)来释放资源,使信号量的值增加 1 ,并唤醒一个等待该信号量的线程或进程。

  

6.2.2 POSIX信号量的使用

  初始化信号量

#include <semaphore.h>int sem_init(sem_t *sem, int pshared, unsigned int value);

  参数:

  pshared:0表示线程间共享,非零表示进程间共享

  value:信号量初始值

  

  销毁信号量

int sem_destroy(sem_t *sem);

  

  等待信号量

int sem_wait(sem_t *sem); //P()

  功能:等待信号量,会将信号量的值减1

  

  发布信号量

int sem_post(sem_t *sem);//V()

  功能:发布信号量,表示资源使用完毕,可以归还资源了。将信号量值加1。

  

6.2.3 环形队列模型实现

  任务继续使用上面的简单模拟计算。

  这是我们实现的环形队列模型:

#pragma once//多生产多消费
#include <iostream>
#include <vector>
#include <ctime>
#include <semaphore.h>
#include <pthread.h>const static int defaultcap=5;template<class T>
class RingQueue
{
private:void P(sem_t &sem) //申请信号量{sem_wait(&sem);}void V(sem_t &sem) //发布信号量{sem_post(&sem);}void Lock(pthread_mutex_t &mutex) //加锁{pthread_mutex_lock(&mutex);}void Unlock(pthread_mutex_t &mutex) //解锁{pthread_mutex_unlock(&mutex);}public:RingQueue(int capacity=defaultcap):_ringqueue(capacity),_capacity(capacity),_c_step(0),_p_step(0){sem_init(&_c_sem,0,0);sem_init(&_p_sem,0,capacity);pthread_mutex_init(&_c_mutex,nullptr);pthread_mutex_init(&_p_mutex,nullptr);}void Push(const T &in) //生产{P(_p_sem);Lock(_p_mutex); //先申请信号量,因为信号量是原子的_ringqueue[_p_step]=in;//向后移动,维持环形特性_p_step++;_p_step%=_capacity;Unlock(_p_mutex);V(_c_sem);}void Pop(T *out) //消费{P(_c_sem);Lock(_c_mutex);*out=_ringqueue[_c_step];//向后移动,维持环形特性_c_step++;_c_step%=_capacity;Unlock(_c_mutex);V(_p_sem);}~RingQueue(){sem_destroy(&_c_sem);sem_destroy(&_p_sem);pthread_mutex_destroy(&_c_mutex);pthread_mutex_destroy(&_p_mutex);}private:std::vector<T> _ringqueue; //模拟环形队列int _capacity; //容量大小int _c_step; //消费者位置int _p_step; //生产者位置 sem_t _c_sem; //消费者信号量,关注的数据资源sem_t _p_sem; //生产者信号量,关注的空间资源pthread_mutex_t _c_mutex;pthread_mutex_t _p_mutex;
};

  

  运行的函数:

#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include "RingQueue.hpp"
#include "Task.hpp"using std::cout;
using std::endl;struct ThreadData
{RingQueue<Task> *rq;std::string threadname;
};void *Productor(void *args)
{//sleep(3);ThreadData *td = static_cast<ThreadData*>(args);RingQueue<Task> *rq = td->rq;std::string name = td->threadname;int len = opers.size();while(true){//获取数据int data1 = rand() % 10 + 1;usleep(10);int data2 = rand() % 10;char op = opers[rand() % len];Task t(data1, data2, op);//生产数据rq->Push(t);cout << "Productor task done, task is : " << t.GetTask() << " who: " << name << endl;sleep(3); }return nullptr;
}void *Consumer(void *args)
{ThreadData *td = static_cast<ThreadData*>(args);RingQueue<Task> *rq = td->rq;std::string name = td->threadname;while(true){//消费数据Task t;rq->Pop(&t);//处理数据t();cout << "Consumer get task, task is : " << t.GetTask() << " who: " << name << " result: " << t.GetResult() << endl;//sleep(1);}return nullptr;
}int main()
{srand(time(nullptr));RingQueue<Task> *rq=new RingQueue<Task>();   pthread_t c[2],p[3];for(int i=0;i<3;i++){ThreadData *td = new ThreadData();td->rq = rq;td->threadname = "Productor-" + std::to_string(i);pthread_create(p+i,nullptr,Productor,td);}for(int i=0;i<2;i++){ThreadData *td = new ThreadData();td->rq = rq;td->threadname = "Consumer-" + std::to_string(i);pthread_create(c+i,nullptr,Consumer,td);}for(int i=0;i<3;i++){pthread_join(p[i],nullptr);}for(int i=0;i<2;i++){pthread_join(c[i],nullptr);}return 0;
}

  

  基于环形队列的生产消费者的模型实现了:

在这里插入图片描述

            

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

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

相关文章

Jackson详解

文章目录 一、Jackson介绍二、基础序列化和反序列化1、快速入门2、序列化API3、反序列化API4、常用配置 三、常用注解1、JsonProperty2、JsonAlias3、JsonIgnore4、JsonIgnoreProperties5、JsonFormat6、JsonPropertyOrder 四、高级特性1、处理泛型1.1、反序列化List泛型1.2、反…

Java 写一个可以持续发送消息的socket服务端

前言 最近在学习flink, 为了模仿一个持续的无界的数据源, 所以需要一个可以持续发送消息的socket服务端. 先上效果图 效果图 socket服务端可以持续的发送消息, flink端是一个统计单词出现总数的消费端,效果图如下 源代码 flink的消费端就不展示了, 需要引入一些依赖和版本…

Linux系统编程基础

Linux操作系统 Linux不是一个具体的操作系统&#xff0c;而是一类操作系统的总称&#xff0c;具体版本成为发行版。 Red Hat&#xff1a;目前被IBM收购&#xff0c;收费版&#xff0c;目前最大的Linux供应商CentOS&#xff1a; Red Hat退出的免费版Ubuntu&#xff1a;界面比较友…

二十一、【机器学习】【非监督学习】- 谱聚类 (Spectral Clustering)​​

系列文章目录 第一章 【机器学习】初识机器学习 第二章 【机器学习】【监督学习】- 逻辑回归算法 (Logistic Regression) 第三章 【机器学习】【监督学习】- 支持向量机 (SVM) 第四章【机器学习】【监督学习】- K-近邻算法 (K-NN) 第五章【机器学习】【监督学习】- 决策树…

hung 之 Android llkd

目录 1. llkd 简介 2. 原理 2.1 内核活锁 2.2 检测机制 2.3 为什么 persistent stack signature 检测机制不执行 ABA 检查&#xff1f; 2.4 为什么 kill 进程后&#xff0c;进程还存在就能判定发生了内核 live-lock&#xff1f; 3. 代码 3.1 内核 live-lock 检查 3.2 …

摸鱼大数据——用户画像——如何给用户“画像”

2、如何给用户“画像” 2.1 什么是标签体系 标签: 是某一种用户特征的符号表示 标签体系: 把用户分到多少类别里面去, 这些类是什么, 彼此之间有什么关系, 就构成了标签体系 标签解决的问题: 解决描述(或命名)问题以及解决数据之间的关联 2.2.1 标签的分类 用户画像标签一…

【附源码】IMX6U嵌入式Linux开发板连接阿里云--MQTT协议

演示 IMX6U嵌入式Linux开发板连接阿里云 阿里云创建设备&&获取LinkSDK 如果还不知道怎么在阿里云创建设备和获取连接阿里云的LinkSDK的话&#xff0c;先看这篇文章&#xff0c;再到这里。看这篇文章的时候&#xff0c;麻烦将下方文章打开对照着看&#xff0c;因为一些…

重测序数据处理得到vcf文件

重测序数据处理得到vcf文件 文章目录 重测序数据处理前言1. 数据是rawdata&#xff0c;需用fastp对数据进行质控和过滤2. 利用getorganelle软件组装叶绿体基因组3. 检查基因组大小&#xff0c;确认是否完整&#xff0c;然后和已知的红毛菜科叶绿体基因组一起构树4. 根据树形结果…

微积分-微分应用2(平均值定理)

要得出平均值定理&#xff0c;我们首先需要以下结果。 罗尔定理 设函数 f f f 满足以下三个假设&#xff1a; f f f 在闭区间 [ a , b ] [a, b] [a,b] 上连续。 f f f 在开区间 ( a , b ) (a, b) (a,b) 上可导。 f ( a ) f ( b ) f(a) f(b) f(a)f(b) 则在开区间 ( a , b …

CTFHUB-SQL注入-UA注入

目录 判断是否存在注入 判断字段数量 判断回显位置 查询数据库名 查询数据库下的表名 查询表中的字段名 查询字段名下的数据 由于本关是UA注入&#xff0c;就不浪费时间判断是什么注入了&#xff0c;在该页面使用 burp工具 抓包&#xff0c;修改User-Agent&#xff0c;加…

JavaScript之Web APIs-DOM

目录 DOM获取元素一、Web API 基本认知1.1 变量声明1.2 作用和分类1.3 DOM树1.4 DOM对象 二、获取DOM对象2.1 通过CSS选择器来获取DOM元素2.2 通过其他方式来获取DOM元素 三、操作元素内容3.1 元素.innerTest属性3.2 元素.innerHTML属性 四、操作元素属性4.1 操作元素常用属性4…

图形编辑器基于Paper.js教程09:鼠标拖动画布,以鼠标点为缩放中心进行视图的缩放

如何使用Paper.js实现画布的缩放与拖动功能 在Web开发中&#xff0c;利用Paper.js库进行图形的绘制和交互操作是一种常见的实践。Paper.js是一个强大的矢量图形库&#xff0c;可以让开发者通过简洁的API完成复杂的图形操作。在本文中&#xff0c;我们将详细探讨如何使用Paper.…

昇思25天学习打卡营第29天 | 基于MindSpore通过GPT实现情感分类

基于MindSpore框架通过GPT模型实现情感分类展示了从项目设置、数据预处理到模型训练和评估的详细步骤&#xff0c;提供了一个完整的案例来理解如何在自然语言处理任务中实现情感分析。 首先&#xff0c;环境配置是任何机器学习项目的起点。项目通过安装特定版本的MindSpore和相…

未来已来:生成式 AI 在对话系统与自主代理中的探索

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a;日常聊聊 ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 前言 正文 一、整体介绍 对话系统&#xff08;Chat&#xff09; 自主代理&#xff08;Agent&#xff09; 二、技术对比 技术差异 优…

安装anaconda后jupyter notebook打不开 闪退

首先&#xff0c;通过清华源安装了最新的anaconda&#xff08;安装在了D盘&#xff09; 尝试打开jupyter&#xff0c;发现小黑框1s后自己关了&#xff0c;根本不打开浏览器 之后尝试按照这个做了一遍https://blog.csdn.net/gary101818/article/details/123560304还是不行。。…

【BUG】已解决:TypeError: Descriptors cannot not be created directly.

已解决&#xff1a;TypeError: Descriptors cannot not be created directly. 目录 已解决&#xff1a;TypeError: Descriptors cannot not be created directly. 【常见模块错误】 【错误原因】 【解决方案】 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来…

创建和管理大量的数据对象:ScriptableObject

一、创建一个继承自ScriptableObject&#xff0c;名为ItemData的类 1、ItemData.cs using UnityEngine;[CreateAssetMenu(menuName "Items/Item")] public class ItemData : ScriptableObject {public string description;public Sprite thumbnail;public GameObj…

数据挖掘-分类和预测

来自&#x1f96c;&#x1f436;程序员 Truraly | 田园 的博客&#xff0c;最新文章首发于&#xff1a;田园幻想乡 | 原文链接 | github &#xff08;欢迎关注&#xff09; 文章目录 概念KNN 算法决策树ID3 算法缺点 C4.5 算法CART 算法 贝叶斯算法朴素贝叶斯算法贝叶斯信念网络…

如何做好结构化逻辑分析:分析之万能公式

有人问&#xff1a;我看了很多书&#xff0c;可是一到分析问题和解决问题时&#xff0c;还是不知如何下手。你能给我一个万能框架吗&#xff1f;这样我遇到问题就可以马上找到思路、直接套用&#xff0c;再也不用让大脑去神游和不知所措了。 我想了想&#xff0c;总结出了这个…

突破•指针二

听说这是目录哦 复习review❤️野指针&#x1fae7;assert断言&#x1fae7;assert的神奇之处 指针的使用和传址调用&#x1fae7;数组名的理解&#x1fae7;理解整个数组和数组首元素地址的区别 使用指针访问数组&#x1fae7;一维数组传参的本质&#x1fae7;二级指针&#x…