Linux——生产者消费者模型

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

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

生产者消费者模型的特点(321原则)

生产者消费者模型是多线程同步与互斥的一个经典场景,其特点如下:

  • 三种关系生产者和生产者(互斥关系)、消费者和消费者(互斥关系)、生产者和消费者(互斥关系、同步关系)。
  • 两种角色 生产者和消费者。(通常由进程或线程承担)
  • 一个交易场所通常指的是内存中的一段缓冲区。(可以自己通过某种方式组织起来)

生产者消费者模型优点

  • 解耦
  • 支持并发
  • 支持忙闲不均

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

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

这里我用STL中的queue实现阻塞队列

#include <iostream>
#include <pthread.h>
#include <queue>
#include <unistd.h>
#define MAX_NUM 5

template<class T>

class BlockQueue
{
    public:
    bool isfull()
    {
        if(_q.size()==MAX_NUM)
        return true;
    }
    bool isempty()
    {
        return _q.empty();
    }
    public:
    BlockQueue(int _cap = MAX_NUM)
    :cap(_cap)
    {
        pthread_mutex_init(&mutex,NULL);
        pthread_cond_init(&full,NULL);
        pthread_cond_init(&empty,NULL);
    }
    void push(const T& data)//从阻塞队列里插入数据(生产者调用)
    {
        pthread_mutex_lock(&mutex);
        while(isfull())
        {
            pthread_cond_wait(&full,&mutex);
        }
        _q.push(data);
        pthread_mutex_unlock(&mutex);
        pthread_cond_signal(&empty);//唤醒在empty条件变量下等待的消费者线程
    }
    void pop(T& data)
    {
        pthread_mutex_lock(&mutex);
        while(isempty())
        {
            pthread_cond_wait(&empty,&mutex);
        }
        data = _q.front();
        _q.pop();
        pthread_mutex_unlock(&mutex);
        pthread_cond_signal(&full);//唤醒在full条件变量下等待的生产者线程
    }

    ~BlockQueue(){
        pthread_mutex_destroy(&mutex);
		pthread_cond_destroy(&full);
		pthread_cond_destroy(&empty);
    }
    private:
    std::queue<T> _q;//阻塞队列
    int cap;//阻塞队列最大的容器数据个数
    pthread_mutex_t mutex;
    pthread_cond_t full;
    pthread_cond_t empty;

};
  • 生产者和消费者共用同一个缓冲区
  • 生产者在生产之前,都要判断一下,缓冲区也就是阻塞队列是否为满,如果满了,就不再生产,进入阻塞等待的状态等待消费者的唤醒信号
  • 消费者在消费之前,都要判断一下,缓冲区是否为空,如果为空,就停止对缓冲区的读取,进入阻塞状态,等待生产者生产后唤醒消费者
  • 阻塞队列是会被生产者和消费者同时访问的临界资源,因此我们需要用一把互斥锁将其保护起来。
  • 这里的数据,我用一个类封装起来,如果可以,类里面可以有很多的数据,方便不同的生产者生产不同的数据信息,不同的消费者也可以选择自己需要的信息(个人想法)
#include"producer.hpp"

class num
{
    public:
    num(){
    }
    num(int _number)
    :number(_number)
    {}
    int getnum()
    {
        return number;
    }
    ~num(){}
    private:
    int number;
};
void* producer(void* args)
{
    BlockQueue<num>* q =(BlockQueue<num>*) args;
    while(1)
    {
        sleep(1);
        num a(rand() % 100 + 1);
		q->push(a); //生产数据
		std::cout << "Producer: " <<a.getnum()<< std::endl;
	}
}
void* consumer(void* args)
{
    BlockQueue<num>* q =(BlockQueue<num>*) args;
    while(1)
    {
        sleep(1);
        num a;
        q->pop(a);
        std::cout << "Consumer: " << a.getnum() << std::endl; //消费数据
		
	}
}
int main()
{
   pthread_t pro, con;
	BlockQueue<int>* bq = new BlockQueue<int>;
	//创建生产者线程和消费者线程
	pthread_create(&pro, nullptr, producer, (void*)bq);
	pthread_create(&con, nullptr, consumer, (void*)bq);

	//join生产者线程和消费者线程
	pthread_join(pro, nullptr);
	pthread_join(con, nullptr);
	delete bq;
	return 0;
}

生产者消费者步调一致

在这里插入图片描述

生产者的速度高于消费者的消费速度

在这里插入图片描述
这里可以观察到,由于生产者的速度很快,缓冲区很快就被生产者的数据填满了,但是到后面又变成同步生产消费了,这是因为生产者想要再进行生产就只能在full条件变量下进行等待,直到消费者消费完一个数据后,生产者才会被唤醒进而继续进行生产,生产者生产完一个数据后又会进行等待。最后的同步速度最终会取决于消费者的消费速度

生产者的速度小于消费者的速度

在这里插入图片描述
开始阻塞队列中是没有数据的,因此消费者只能在empty条件变量下进行等待,直到生产者生产完一个数据后,消费者才会被唤醒进而进行消费,消费者消费完这一个数据后又会进行等待,所以生产者和消费者的步调就是一致的。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

硬核分享|使用AI模型给黑白照片上色与高清修复

硬核分享|使用AI模型给黑白照片上色与高清修复_哔哩哔哩_bilibili 本文介绍了如何修复褪色、模糊或损坏的图片以及老旧照片上色的工具及使用方法&#xff1b; 低清修复前 高清修复后 我们在日常生活中可能会频繁地接触到一些历史悠久、画面模糊甚至破损严重的照片。这类照片往往…

使用Intellij idea编写Spark应用程序(Scala+Maven)

使用Intellij idea编写Spark应用程序(ScalaMaven) 对Scala代码进行打包编译时&#xff0c;可以采用Maven&#xff0c;也可以采用sbt&#xff0c;相对而言&#xff0c;业界更多使用sbt。这里介绍IntelliJ IDEA和Maven的组合使用方法。IntelliJ IDEA和SBT的组合使用方法&#xf…

如何使用OpenHarmony实现一个模拟应用首次启动

应用首次启动&#xff08;ArkTS&#xff09; 介绍 本篇Codelab基于自定义弹框、首选项和页面路由实现一个模拟应用首次启动的案例。需要完成以下功能&#xff1a; 实现四个页面&#xff0c;启动页、隐私协议页、广告页、应用首页。页面之间的跳转。实现自定义隐私协议弹窗&a…

JAVA实战开源项目:大病保险管理系统(Vue+SpringBoot)

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 系统配置维护2.2 系统参保管理2.3 大病保险管理2.4 大病登记管理2.5 保险审核管理 三、系统详细设计3.1 系统整体配置功能设计3.2 大病人员模块设计3.3 大病保险模块设计3.4 大病登记模块设计3.5 保险审核模块设计 四、…

C程序编译、链接与项目构建

C程序编译、链接与项目构建 摘要C编译环境静、动态库介绍gcc与g和程序编译、链接Visual Studio创建和链接库动态库的显示调用 Make介绍安装使用 CMake介绍安装使用构建方式内部构建外部构建构建使用静/动态库常用[系统]变量常用指令CMake模块 Make与CMake的联系与区别 摘要 本…

优化选址问题 | 基于鹈鹕算法求解基站选址问题含Matlab源码

目录 问题代码问题 鹈鹕算法(Pelican Optimization Algorithm, POA)是一种相对较新的启发式优化算法,模拟了鹈鹕鸟觅食的行为。这种算法通常用于解决复杂的优化问题,如函数优化、路径规划、调度问题等。基站选址问题通常是一个复杂的优化问题,需要考虑覆盖范围、干扰、成…

迷宫(一)(DFS BFS)

//新生训练 #include <bits/stdc.h> using namespace std; int n, m; bool f; char mp[15][15]; int vis[15][15]; int dir[4][2] {{-1, 0}, {0, 1}, {1, 0}, {0, -1}}; bool in(int x, int y) {return 0 < x && x < n && 0 < y && y …

kali安装docker(亲测有效)

第一步&#xff1a;添加Docker官方的GPG密钥 curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add - 第二步&#xff1a; 第二步更新源 echo deb https://download.docker.com/linux/debian stretch stable> /etc/apt/sources.list.d/docker.list…

基于python+vue超市在线销售系统的设计与实现flask-django-php-nodejs

根据此问题&#xff0c;研发一套超市在线销售系统&#xff0c;既能够大大提高信息的检索、变更与维护的工作效率&#xff0c;也能够方便信息系统的管理运用&#xff0c;从而减少信息管理成本&#xff0c;提高效率。 该超市在线销售系统采用B/S架构、并采用python语言以及django…

图床项目实战:后续开发与优化

在之前的文章中&#xff0c;我们介绍了图床项目的基本实现&#xff0c;接下来&#xff0c;我将提供扩展功能和优化性能的关键代码片段。 一、图片分类管理 首先&#xff0c;我们需要在数据库中创建分类表&#xff0c;并在图片表中添加分类字段。 class Category(db.Model): …

生物信息学文章中常见的图应该怎么看?

目录 火山图 热图 箱线图 森林图 LASSO回归可视化图&#xff08;套索图&#xff09; 交叉验证图 PCA图 ROC曲线图 这篇文章只介绍这些图应该怎么解读&#xff0c;具体怎么绘制&#xff0c;需要什么参数&#xff0c;怎么处理数据&#xff0c;会在下一篇文章里面给出 火山…

AIGC——ComfyUI SDXL多种风格预设提示词插件安装与使用

概述 SDXL Prompt Styler可以预先给SDXL模型提供了各种预设风格的提示词插件&#xff0c;相当于预先设定好了多种不同风格的词语。使用这个插件&#xff0c;只需从中选取所需的风格&#xff0c;它会自动将选定的风格词汇添加到我们的提示中。 安装 插件地址&#xff1a;http…

使用双异步后,从 191s 优化到 2s

使用双异步后&#xff0c;从 191s 优化到 2s 一般我会这样做&#xff1a; 通过POI读取需要导入的Excel&#xff1b; 以文件名为表名、列头为列名、并将数据拼接成sql&#xff1b; 通过JDBC或mybatis插入数据库&#xff1b; 操作起来&#xff0c;如果文件比较多&#xff0…

springboot精品源码

springboot精品源码 所有项目都包括&#xff1a;源码数据库文件开题LW说明文档运行视频 请看主页资料联系。 项目类型包括: 1 SpringBoot学生心理咨询评估系统 2 基于SpringBoot的网上订餐系统 3 大学生租房平台的设计与实现 4 SpringBoot房屋租赁系统 5 基于SpringBoot的课…

tcp 协议详解

什么是 TCP 协议 TCP全称为 “传输控制协议(Transmission Control Protocol”). 人如其名, 要对数据的传输进行一个详细的控制。TCP 是一个传输层的协议。 如下图&#xff1a; 我们接下来在讲解 TCP/IP 协议栈的下三层时都会先解决这两个问题&#xff1a; 报头与有效载荷如何…

大数据------javase基础------day18(完结)

类加载器 作用 负责将编译后的java文件&#xff08;即.class文件&#xff09;加载到内存中供虚拟机执行 类加载的时机------总结一句话&#xff1a;用到类就加载&#xff0c;不用就不加载 创建类的实例调用类的方法访问类或者接口的类变量&#xff0c;或者为该类变量赋值使用反…

阿里云幻兽帕鲁4核16G和8核32G服务器优惠价格

2024阿里云幻兽帕鲁专用服务器价格表&#xff1a;4核16G幻兽帕鲁专用服务器26元一个月、149元半年&#xff0c;默认10M公网带宽&#xff0c;8核32G幻兽帕鲁服务器10M带宽价格90元1个月、271元3个月。阿里云提供的Palworld服务器是ECS经济型e实例&#xff0c;CPU采用Intel Xeon …

Linux:详解https协议

文章目录 什么是https协议信息窃取常见的加密数据摘要和数据指纹https的工作过程只使用对称加密只使用非对称加密都使用非对称加密非对称加密对称加密 证书数据签名https方案 本篇要总结的内容是关于https协议的相关内容 什么是https协议 在讲述https协议之前&#xff0c;首先…

差分约束系统

差分约束系统 差分约束系统&#xff08;spfa&#xff09;1、概述2、过程模拟3、推理 差分约束系统&#xff08;spfa&#xff09; 1、概述 x j − x i ≤ w k x_j-x_i\le w_k xj​−xi​≤wk​转换为&#xff1a; x j ≤ w k x i x_j\le w_kx_i xj​≤wk​xi​ 在松弛操作中&…

dubbo 源码系列之-集群三板斧---负载均衡(-)

dubbo 源码系列之-负载均衡 概述核心接口 LoadBalanceDubbo 提供了 5 种负载均衡实现&#xff0c;分别是&#xff1a;LoadBalance 接口AbstractLoadBalance ConsistentHashLoadBalance 一致性hash1. 一致性 Hash 简析1.0 hash 算法2.0 一致性Hash算法3.0 一致性hash算法 引入槽…