【多线程】生产者消费者模型(代码实现)

news2024/11/15 3:53:34

文章目录

  • 生产者消费者模型介绍
  • 生产者消费者模型的特点
  • 基于BlockingQueue的生产者消费者模型

生产者消费者模型介绍

生产者消费模型是一种常见的多线程编程模式,广泛应用于解决并发编程中的数据共享和任务调度问题。在该模型中,我们将生产数据并放入缓冲区的线程称为生产者线程,反之,从缓冲区中获取数据的线程就称为消费者线程。生产者和消费者通过共享的缓冲区进行通信,并且使用同步机制(如互斥锁和条件变量)来协调操作,防止数据竞争和资源浪费。
缓冲区是指一个用来存放数据的数据结构,通常是一个队列。 为了便于记忆,可以将生产者消费模型看成是由1个共享区(缓冲区),2个角色(生产者和消费者)和3种关系(生产者和生产者,消费者和生产者,消费者和消费者)组成
在这里插入图片描述
生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。这个阻塞队列就是用来给生产者和消费者解耦的

生产者消费者模型的特点

  • 解耦:生产者和消费者不直接接触,彼此解耦,独立性强
  • 效率高:通过缓冲区和同步机制,避免了忙等待
  • 灵活性:可以通过调整缓冲区大小和线程数量来优化系统性能。

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

BlockingQueue本质就是一个队列,常用来实现生产者消费者模型。在队列为空时获取元素的消费者线程就会被阻塞,直到队列中被生产者放入数据并唤醒阻塞的消费者线程。同样的,如果队列为满,那么此时不能再生产数据,生产者线程就会阻塞,直到消费者从队列中拿走数据并唤醒等待的生产者线程。
在这里插入图片描述
下面我们用代码模拟生产者消费者模型:

  • blockingqueue.hpp头文件,定义并实现了了阻塞队列的基本功能
#pragma once
#include <iostream>
#include <functional>
#include <string>
#include <queue>
#include <pthread.h>

using namespace std;
const static int defaultcap = 5;

template <class T>

class BlockingQueue
{
private:
    // 判断是否为空
    bool IsEmpty()
    {
        return _block_queue.empty();
    }
    // 判断是否为满
    bool IsFull()
    {
        return _block_queue.size() == _maxcap;
    }

public:
    // 构造
    BlockingQueue(int cap = defaultcap)
        : _maxcap(cap)
    {
        pthread_mutex_init(&_mutex, NULL);
        pthread_cond_init(&_p_cond, NULL);
        pthread_cond_init(&_c_cond, NULL);
    }
    // 弹出元素,即消费者取出元素
    void Pop(T *out)
    {
        pthread_mutex_lock(&_mutex);
        while (IsEmpty())
        { // 队列为空没有元素,线程进入等待
            pthread_cond_wait(&_c_cond, &_mutex);
        }
        // 此时队列一定有元素
        *out = _block_queue.front();
        _block_queue.pop();
        pthread_cond_signal(&_p_cond); // 唤醒一个生产者
        pthread_mutex_unlock(&_mutex);
    }

    // 入队列,生产者生产元素
    void Push(const T &val)
    {
        pthread_mutex_lock(&_mutex);
        while (IsFull())
        { // 队列为满,生产者线程进入等待
            pthread_cond_wait(&_p_cond, &_mutex);
        }
        // 此时队列一定不为满
        _block_queue.push(val);
        pthread_cond_signal(&_c_cond); // 唤醒一个消费者
        pthread_mutex_unlock(&_mutex);
    }

    // 析构
    ~BlockingQueue()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_p_cond);
        pthread_cond_destroy(&_c_cond);
    }

private:
    queue<T> _block_queue;  // 底层容器存储临界资源
    int _maxcap;            // 队列最大容量
    pthread_mutex_t _mutex; // 互斥锁,实现同步
    pthread_cond_t _p_cond; // 生产者条件变量
    pthread_cond_t _c_cond; // 消费者条件变量
};

  • main.cpp文件,定义了生产者线程和消费者线程的执行逻辑
#include <iostream>
#include "blockingqueue.hpp"
#include <unistd.h>
#include <ctime>

using namespace std;

void *Consumer(void *arg)
{
    BlockingQueue<int> *bq = static_cast<BlockingQueue<int> *>(arg); // 类型转换
    while (true)
    {
        int val = 0;
        bq->Pop(&val);
        cout << "消费者取出数据->" << val << endl;
        sleep(1);
    }
}

void *Produce(void *arg)
{
    BlockingQueue<int> *bq = static_cast<BlockingQueue<int> *>(arg); // 类型转换
    while (true)
    {
        int val = rand() % 100;
        bq->Push(val);
        cout << "生产者生产数据->" << val << endl;
        sleep(1);
    }
}

int main()
{
    BlockingQueue<int> *bq = new BlockingQueue<int>();
    srand(time(nullptr));
    pthread_t t1, t2;
    pthread_create(&t1, NULL, Consumer, bq);
    pthread_create(&t2, NULL, Produce, bq);

    pthread_join(t1, nullptr);
    pthread_join(t2, nullptr);
    return 0;
}
  • 运行结果
    在这里插入图片描述

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

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

相关文章

“都市绿洲,健康生活新纪元“ —全方位打造高科技多功能智慧健康园

咸宁&#xff0c;这座被誉为"桂花之乡"和"华中康养区"的城市&#xff0c;如同一朵绽放在湖北东南的璀璨明珠。这里桂花飘香&#xff0c;温泉涌动&#xff0c;将自然的馈赠与人文的智慧完美融合。漫步在咸宁&#xff0c;你会被满城金黄的桂花树所吸引&#…

SCSA第四天

ASPF FTP --- 文件传输协议 Tftp --- 简单文件传输协议 FTP协议相较于Tftp协议 ---- 1&#xff0c;需要进行认证 2&#xff0c;拥有一套完整的命令集 用户认证 防火墙管理员认证 ---- 校验登录者身份合法性 用户认证 --- 上网行为管理中的一环 上网用户认证 --- 三层认证…

QImage显示图片像素

在Qt中&#xff0c;QImage 类是用来表示和处理图像的。如果你想查看或显示一个图片的像素数据&#xff0c;你可以使用 QImage 提供的方法来访问这些数据。以下是一些基本的方法来获取和显示图片的像素信息&#xff1a; 获取图像的像素格式&#xff1a; 使用 QImage::format() …

redis相关知识记录

redis基本数据类型 Redis⽀持五种主要数据结构&#xff1a;字符串&#xff08;Strings&#xff09;、列表&#xff08;Lists&#xff09;、哈希表&#xff08;Hashes&#xff09;、集合&#xff08;Sets&#xff09;和有序集合&#xff08;Sorted Sets&#xff09;。这些数据结…

springboot 旅游导航系统-计算机毕业设计源码69476

目 录 第 1 章 引 言 1.1 选题背景 1.2 研究现状 1.3 论文结构安排 第 2 章 系统的需求分析 2.1 系统可行性分析 2.1.1 技术方面可行性分析 2.1.2 经济方面可行性分析 2.1.3 法律方面可行性分析 2.1.4 操作方面可行性分析 2.2 系统功能需求分析 2.3 系统性需求分析…

Flutter——最详细(Table)网格、表格组件使用教程

背景 用于展示表格组件&#xff0c;可指定线宽、列宽、文字方向等属性 属性作用columnWidths列的宽度defaultVerticalAlignment网格内部组件摆放方向border网格样式修改children表格里面的组件textDirection文本排序方向 import package:flutter/material.dart;class CustomTa…

c#调用c++ dll库报错System.BadImageFormatException

System.BadImageFormatException:“试图加载格式不正确的程序。 (异常来自 HRESULT:0x8007000B)” 1. dll需要选择release模式进行编译 2.选择相同位数&#xff0c;比如x64平台&#xff0c;c#也需要x64 3.不要设置c#不支持的函数供调用 比如&#xff1a; c可以输出到控制台…

Lumos学习王佩丰Excel第四讲:排序与选择

一、排序 1、简单排序&#xff1a;不要选中一列排序&#xff0c;不然只是局部排序&#xff0c;其他数据都会发生错乱。 2、多条件排序 3、2003版本中超过3个排序条件时如何处理&#xff1a;从最后一个条件到第一个条件倒着按照要求依次排序。 4、按颜色排序 5、自定义排序次序…

洛谷P10716【MX-X1-T4】「KDOI-05」简单的字符串问题(扩展kmp+set+二分+扫描线树状数组)

题目 思路来源 小羊肖恩 题解 羊神这个做法tql&#xff0c;当时只是机械地写&#xff0c;过了之后再想想&#xff0c;才觉得确实是nb 先扩展kmp&#xff08;Z函数&#xff09;预处理出来数组&#xff0c;记z[i]为i往后可以和前缀匹配的最大长度 对于每个询问(p,cnt)&#x…

Dify工作流中的变量聚合节点

一.定义 变量聚合节点&#xff08;原变量赋值节点&#xff09;负责整合不同分支的输出结果&#xff0c;确保无论哪个分支被执行&#xff0c;其结果都能通过一个统一的变量来引用和访问。这在多分支的情况下非常有用&#xff0c;可将不同分支下相同作用的变量映射为一个输出变量…

esp32硬件电路设计

ESP-IDF 入门指南 | 乐鑫科技 (espressif.com) ESP32-DevKitC V4 入门指南 - ESP32 - — ESP-IDF 编程指南 v5.1 文档 (espressif.com)

matlab仿真 模拟调制(上)

&#xff08;内容源自详解MATLAB&#xff0f;SIMULINK 通信系统建模与仿真 刘学勇编著第五章内容&#xff0c;有兴趣的读者请阅读原书&#xff09; 1.幅度调制 clear all ts0.0025; %信号抽样时间间隔 t0:ts:10-ts;%时间矢量 fs1/ts;%抽样频率 dffs/length(t); %fft的频率分…

Linux进程间通信:匿名管道 命名管道

Linux进程间通信&#xff1a;匿名管道 &命名管道 一、进程间通信目的二、什么是管道三、匿名管道创建3.1 系统调用原型3.2 匿名管道创建 四、内核创建匿名管道过程五、匿名管道性质5.1 匿名管道的4种特殊情况5.2 匿名管道的5种特性5.3 测试源代码 六、命名管道6.1 创建命名…

高中数学:立体几何-基本立体图形分类

一、常见空间几何体 二、多面体 1、棱柱 2、棱锥 3、棱台 4、相关关系 三、旋转体 1、圆柱 2、圆锥 3、圆台 4、球

LLM 模型压缩之一 | APT

0. 资源链接 论文: APT: Adaptive Pruning and Tuning Pretrained Language Models for Efficient Training and Inference. 项目: https://github.com/ROIM1998/APT 1.背景动机 现有的大模型压缩加速存在以下问题&#xff1a; PEFT: 可以低成本为下游任务微调&#xff0c;…

游戏AI的创造思路-技术基础-蒙特卡洛树搜索(1)

本篇介绍蒙特卡洛树搜索算法&#xff0c;AlphaGo用于围棋计算的应用就是基于蒙特卡洛树搜索研发的~~~ 目录 1. 定义 2. 发展历史 3. 公式和函数 3.1.算法的公式和函数 3.2. Python实现公式和函数 4. 运行原理 4.1. 运行原理 4.2. 各步骤用Python代码 5. 优缺点和缺陷的…

免费听书TV版v1.0.1

使用非常稳定流畅&#xff0c;UI界面设计美观简洁&#xff0c;纯净无广。资源虽然不是特别多&#xff0c;但是日常听书还是可以满足需求。 完全免费&#xff0c;操作简单方便&#xff0c;安装即用&#xff0c;没有任何限制。 可以适配遥控器操作&#xff0c;OK键开启或关闭语…

适配各种IT场景的零信任沙箱

在当今数字化时代&#xff0c;网络安全威胁层出不穷&#xff0c;传统的安全防御策略已难以应对日益复杂的网络攻击。零信任与沙箱技术的结合&#xff0c;作为一种新兴的安全防护策略&#xff0c;正逐渐受到企业和组织的青睐。本文将深入探讨零信任结合沙箱技术所能解决的问题及…

【智能算法改进】一种混合多策略改进的麻雀搜索算法

目录 1.算法原理2.改进点3.结果展示4.参考文献5.代码获取 1.算法原理 【智能算法】麻雀搜索算法&#xff08;SSA&#xff09;原理及实现 2.改进点 精英反向学习策略 将精英反向学习策略应用到初始化阶段, 通过反向解的生成与精英个体的选择, 不仅使算法搜索范围得到扩大, 提…

C++第四弹 -- 类与对象(中上) (构造函数 析构函数 拷贝构造函数)

目录 前言构造函数1. 概念2. 特征 析构函数1. 概念2. 特征 拷贝构造函数1. 概念2. 特征 总结 前言 让我们一起揭开 C 对象生命周期管理的神秘面纱&#xff0c;掌握构造函数、析构函数和拷贝构造函数的精髓&#xff01; 博客主页: 酷酷学!!! 期待更多好文, 点击关注~ 构造函…