【lesson56】生产者消费者模型

news2025/2/23 0:20:04

文章目录

  • 学习生产者消费者模型过程中要回答的两个问题
  • 生产者消费者模型的概念
  • 基于阻塞队列的生产者消费者模型
  • 编码实现
    • Common.h
    • LockGuard.hpp
    • Condtion.hpp
    • BlockQueue.hpp
    • Task.hpp
    • ConProd.cc

学习生产者消费者模型过程中要回答的两个问题

1.条件变量是在条件满足的时候,会唤醒指定的线程---->我们怎么知道条件是否满足呢?
2.mutex的意义

生产者消费者模型的概念

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

生产者消费者模型:
在这里插入图片描述
生产者消费者模型优点
解耦
支持并发
支持忙闲不均

上面的优点全都是因为有一个仓库的存在。
生产者消费者模型类比:
在这里插入图片描述
消费者去超市买商品。
厂家往超市放商品
而这就是生产者消费者模型它的核心意义是提高了效率,并且通过超市让消费者和厂家进行了解耦合的关系,不用让消费者和厂家进行沟通。

超市本质就是一个商品缓冲区。

生产者消费者模型有:
2种角色:生产者和消费者
1种交易场所:超市
3中关系:
生产者和生产者-----竞争且互斥
消费者和消费者----竞争且互斥
生产者和消费者----互斥且同步

消费者和生产者由线程承担----给线程进行角色化
超市:某种数据结构—表示缓冲区
商品:数据

生产者生产的数据是从哪里来的?
消费者如何使用发送过来的数据?
而在现实生活中生产数据和发送数据的过程都需要花时间。

1.条件变量是在条件满足的时候,会唤醒指定的线程---->我们怎么知道条件是否满足呢?消费者和生产者各自知道。
超市没有商品了,消费者知道。(因为消费者去超市买不到商品)
超市商品满了,生产者知道(因为生产者刚想超市放了一堆商品)
2.mutex的意义
保护临界资源

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

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

编码实现

Common.h

#pragma once
#include <iostream>
#include <pthread.h>
#include <queue>
#include <unistd.h>
#include <ctime>
#include <functional>


#define gDefaultCap  5//阻塞队列默认的数据个数
#define CTNUM 1//消费者线程个数
#define PTNUM 1//生产者线程个数

LockGuard.hpp

//自己封装的锁,定义后会自动初始化和销毁
#pragma once
#include "Common.h"

class Mutex
{
public:
    Mutex()
    {
        pthread_mutex_init(&_mtx,nullptr);
    }

    pthread_mutex_t& GetMutex()
    {
        return _mtx;
    }

    ~Mutex()
    {
        pthread_mutex_destroy(&_mtx);
    }
private:
    pthread_mutex_t _mtx;
};

//RAII风格,会自动加锁和解锁
class LockGuard
{
public:
    LockGuard(Mutex* mtx)
    :_mtx(mtx)
    {
        pthread_mutex_lock(&_mtx->GetMutex());
        //std::cout << "lock" << std::endl;
    }


    ~LockGuard()
    {
        pthread_mutex_unlock(&_mtx->GetMutex());
        //std::cout << "unlock" << std::endl;
    }
private:
    Mutex* _mtx;
};

Condtion.hpp

#include "Common.h"
//自己封装的条件变量,定义后会自动初始化和销毁
class Condtion
{
public:
    Condtion()
    {
        pthread_cond_init(&_cond,nullptr);
    }

    pthread_cond_t& GetCond()
    {
        return _cond;
    }

    ~Condtion()
    {
        pthread_cond_destroy(&_cond);
    }
private:
    pthread_cond_t _cond;
};

BlockQueue.hpp

#pragma once
#include "Common.h"
#include "LockGuard.hpp"
#include "Condtion.hpp"

template<class T>
class BlockQueue
{
private:
    bool Empty()
    {
        return bq.size() == 0;
    }
    bool Full()
    {
        return bq.size() == _capacity;
    }
public:
    BlockQueue(int capacity = gDefaultCap)
    :_capacity(capacity)
    {}

    void Push(T& in)
    {
        //RAII风格加锁,会自动解锁
        LockGuard lockgrard(&_mtx);
        //条件不满足就一直等待
        while(Full())
        {
            pthread_cond_wait(&_FullCond.GetCond(),&_mtx.GetMutex());
        }
        bq.push(in);
        //唤醒消费者线程
        pthread_cond_signal(&_EmptyCond.GetCond());
    }
    void Pop(T* out)
    {
        //RAII风格加锁,会自动解锁
        LockGuard lockgrard(&_mtx);
        //条件不满足就一直等待
        while(Empty())
        {
            pthread_cond_wait(&_EmptyCond.GetCond(),&_mtx.GetMutex());
        }
        *out = bq.front();
        bq.pop();
        //唤醒生产者线程
        pthread_cond_signal(&_FullCond.GetCond());

    }

    ~BlockQueue()
    {}
private:
    std::queue<T> bq;//阻塞队列
    int _capacity;//阻塞队列的最大容量
    Mutex _mtx;//自己封装的锁,会自动初始化和销毁
    Condtion _EmptyCond;//自己封装的条件变量,会自动初始化和销毁
    Condtion _FullCond;//自己封装的条件变量,会自动初始化和销毁
};

Task.hpp

#pragma once
#include "Common.h"

typedef std::function<int(int, int)> func_t;

class Task
{
public:
    Task()
    {}
    
    Task(int x, int y, func_t func)
        : _x(x),
          _y(y),
          _func(func)
    {}
	
	//仿函数
    int operator()()
    {
        return _func(_x, _y);
    }

public:
    int _x;
    int _y;
    func_t _func;
};

ConProd.cc

#include "Common.h"
#include "BlockQueue.hpp"
#include "Task.hpp"

int myAdd(int x,int y)
{
    return x+y;
}

void* ConsumerRoutine(void* args)
{
    BlockQueue<Task>* bq = (BlockQueue<Task>*)args;

    Task t;
    while(true)
    {
        //获取一个任务
        bq->Pop(&t);
        std::cout << "[" << pthread_self() << "] 获取一个任务:" << t._x << " + " << t._y << " = " << t() << std::endl;
        sleep(1);
    }
}
void* ProducterRoutine(void* args)
{
    BlockQueue<Task>* bq = (BlockQueue<Task>*)args;
    while(true)
    {
        //制作一个任务
        int x = rand() % 100 + 1;
        int y = rand()%30 + 1;
        
        Task t(x,y,myAdd);
        std::cout << "[" << pthread_self() << "] 制作一个任务:" << x << " + " << y << " =?" << std::endl;
        bq->Push(t);
        sleep(1);
    }
}
int main()
{
    srand((unsigned int)time(nullptr) ^ getpid() ^ 0X3333);

    //定义消费者线程和生成者线程的id数组
    pthread_t Consumer[CTNUM];
    pthread_t Producter[PTNUM];

    //创建阻塞队列
    BlockQueue<Task>* bqueue = new BlockQueue<Task>;

    //创建消费者线程
    for(int i = 0; i < CTNUM; i++)
    {
        pthread_create(Consumer + i,nullptr,ConsumerRoutine,(void*)bqueue);
    }
   
   //创建生产者线程
   for(int i = 0; i < PTNUM; i++)
    {
        pthread_create(Producter + i,nullptr,ProducterRoutine,(void*)bqueue);
    }
    
    //等待消费者线程
    for(int i = 0; i < CTNUM; i++)
    {
        pthread_join(Consumer[i],nullptr);
    }
    
    //等待生产者线程
    for(int i = 0; i < CTNUM; i++)
    {
        pthread_join(Producter[i],nullptr);
    }
    

    return 0;
}

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

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

相关文章

使用 XML 和 YAML 文件的文件输入和输出

目标 您将找到以下问题的答案&#xff1a; 如何使用YAML或XML文件打印和读取文件和OpenCV的文本条目&#xff1f;如何对 OpenCV 数据结构做同样的事情&#xff1f;如何为您的数据结构执行此操作&#xff1f;使用 OpenCV 数据结构&#xff0c;例如 cv&#xff1a;&#xff1a;…

SolidWorks如何在一个零件的基础上绘制另一个零件

经过测试&#xff0c;新建零件&#xff0c;然后插入零件a&#xff0c;在a的基础上绘制b,这种做法无法断开a与b的联系。虽然可以通过切除命令&#xff0c;切除b&#xff0c;但不是正途。 在装配体中可以实现&#xff1a; &#xff08;1&#xff09;建立装配体 &#xff08;2&…

在线黑色响应式全屏滚动主页html源码

html5黑色大气的个人博客全屏滚动个人主页源码 右键记事本即可修改 直接上传服务器空间就可使用

【HTML】过年不能放烟花,那就放电子烟花

闲谈 大家回家过年可能都多多少少放过些&#x1f9e8;&#xff0c;但是有些在城市上过年的小伙伴可能就没有机会放鞭炮了。不过没关系&#xff0c;我们懂技术&#xff0c;我们用技术自娱自乐&#xff0c;放电子烟花&#xff0c;总不可能被警长叔叔敲门问候吧。 开干 首先&…

一个比SDXL更快的模型——Stable Cascade【必坑指北】

2024年的春节假期&#xff0c;AIGC界又发生了重大革命性事件。 OpenAI 发布了首款文生视频模型——Sora。简单来说就是&#xff0c;AI视频要变天了&#xff01;之前的SVD&#xff0c;还是Google的Lumiere最多就几十帧&#xff0c;大约十秒左右&#xff0c;但是Sora却是SOTA级别…

文生视频:Sora模型报告总结

作为世界模拟器的视频生成模型 我们探索视频数据生成模型的大规模训练。具体来说&#xff0c;我们在可变持续时间、分辨率和宽高比的视频和图像上联合训练文本条件扩散模型。我们利用对视频和图像潜在代码的时空补丁进行操作的变压器架构。我们最大的模型 Sora 能够生成一分钟…

BUGKU-WEB 网站被黑

题目描述 题目截图如下&#xff1a; 进入场景看看&#xff1a; 解题思路 提示说&#xff1a; 网站被黑了 黑客会不会留下后门&#xff08;那就是留了&#xff09;那就扫描目录先看看再说发现确实有登入界面&#xff0c;但是密码不知道&#xff08;爆破走起&#xff09; …

Matplotlib plt.plot:从入门到精通,只需一篇文章!

Matplotlib plt.plot&#xff1a;从入门到精通&#xff0c;只需一篇文章&#xff01; 利用Matplotlib进行数据可视化示例 &#x1f335;文章目录&#x1f335; &#x1f4ca; 1. 引言&#xff1a;为什么Matplotlib在数据可视化中如此重要&#xff1f;&#x1f4ca;✨ 2. plt.pl…

【LLM-RAG】BGE M3-embedding模型(模型篇|混合检索、多阶段训练)

note M3-Embedding联合了3种常用的检索方式&#xff0c;对应三种不同的文本相似度计算方法。可以基于这三种检索方式进行多路召回相关文档&#xff0c;然后基于三种相似度得分平均求和对召回结果做进一步重排。 多阶段训练过程&#xff1a; 第一阶段&#xff1a;第一阶段的自…

C语言希尔排序详解!!!速过

目录 希尔排序是什么&#xff1f; 关于时间复杂度 希尔排序的源代码 希尔排序源代码的详解 希尔排序是什么&#xff1f; 之前我们说了三个排序&#xff08;插入排序&#xff0c;选择排序&#xff0c;冒泡排序&#xff09;有需要的铁铁可以去看看之前的讲解。 但因为之前的…

政安晨:【示例演绎】【Python】【Numpy数据处理】快速入门(二)

环境准备 大家如果第一次看到&#xff0c;可以先从我这个演绎系列的第一篇文章开始&#xff0c;包括准备环境等等。 第一篇文章如下&#xff1a; 政安晨&#xff1a;【示例演绎】【Python】【Numpy数据处理】快速入门&#xff08;一&#xff09;https://blog.csdn.net/snowd…

结构体对齐规则及为什么会有结构体对齐

前言&#xff1a; 大家在学习结构体中&#xff0c;在计算结构体大小时想必会很疑惑&#xff0c;为什么结构体的大小不是按照常理像数组一样一个字节一个字节的挨在一起放&#xff1f;今天带大家一起深入探讨一下背后的规则和原因。 结构体对齐规则&#xff1a; 结构体对齐其实…

Kotlin基本语法2基本内置方法

1.Kotlin的可空性 fun main() {var str:String? "butterfly" //?问好代表可空类型str null } 安全的管理 1.1 安全操作调用符 fun main() {var str:String? "butterfly" //?问好代表可空类型str nullprintln(str?.capitalize())//当String为null时…

Windows11系统下对jar文件解压修改后在压缩为jar文件

一、准备内容 安装JAVA环境——若已安装则忽略 我这里以在Windows11中安装JAVA 的JDK8环境为例进行安装配置说明: 1.1、下载JDK安装包 Java Downloads | Oraclehttps://www.oracle.com/java/technologies/downloads/#java8-windows 1.2、安装JDK

流量主小程序/公众号h5开源代码 源码分享

小程序开源代码合集 1、网课搜题小程序源码/小猿题库多接口微信小程序源码自带流量主 搭建教程 1、微信公众平台注册自己的小程序 2、下载微信开发者工具和小程序的源码 3、上传代码到自己的小程序 界面截图&#xff1a; 开源项目地址&#xff1a;https://ms3.ishenglu.com…

【计算机网络】物理层|传输介质|物理层设备|宽带接入技术

目录 一、思维导图 二、传输介质 1.传输介质——导引型 2.传输介质——非导引型​编辑 三、物理层设备 1.物理层设备&#xff1a;中继器&集线器 2.宽带接入技术&#xff08;有线&#xff09; ​编辑 四、趁热打铁☞习题训练 五、物理层总思维导图 推荐 前些天发现…

【电路笔记】-LR串联电路

LR串联电路 文章目录 LR串联电路1、概述2、示例1所有线圈、电感器、扼流圈和变压器都会在其周围产生磁场,由电感与电阻串联组成,形成 LR 串联电路。 1、概述 在本节有关电感器的第一个文章中,我们简要介绍了电感器的时间常数,指出流过电感器的电流不会瞬时变化,而是会以恒…

常见的几种Web安全问题测试简介

Web项目比较常见的安全问题 1.XSS(CrossSite Script)跨站脚本攻击 XSS(CrossSite Script)跨站脚本攻击。它指的是恶意攻击者往Web 页面里插入恶意html代码&#xff0c;当用户浏览该页之时&#xff0c;嵌入其中Web 里面的html 代码会被执行&#xff0c;从而达到恶意用户的特殊…

【Docker】Docker Container操作案例 | 综合实战

文章目录 Docker Container操作案例容器的基本操作容器状态迁移容器批量处理技巧容器交互模式attached模式detached模式interactive模式 容器与宿主机内容复制容器自动删除容器自动重启容器环境变量设置容器详情查看容器执行单行命令容器镜像导入导出容器日志查看容器资源查看 …

[AIGC] 消息积压了,该如何处理?

在构建分布式系统时&#xff0c;开发人员经常会遇到消息积压的问题。当系统的处理能力不足时&#xff0c;消息会在队列中积压&#xff0c;导致系统 slowed down 或 even crashed。为了解决这个问题&#xff0c;我们需要采取一些措施来缓解消息积压。 文章目录 什么是消息积压&…