【Linux —— 生产者消费者模型】

news2024/9/20 14:31:27

Linux —— 生产者消费者模型

  • 生产者消费者模型概述
  • 生产者消费者模型特点
  • 生产者消费者模型优点
  • 基于BlockingQueue的生产者消费者模型

生产者消费者模型概述

生产者消费者模型是一种并发编程模型,用于解决多线程或多进程间的数据共享和同步问题。在这个模型中,有两种角色:生产者和消费者,它们通过共享的缓冲区进行通信。生产者负责生成数据并将其放入缓冲区,而消费者则从缓冲区中获取数据并进行处理。

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

在这里插入图片描述

生产者消费者模型特点

  1. 保证生产者不会在缓冲区满的时候继续向缓冲区放入数据, 而消费者也不会在缓冲区空的时候,消耗数据。
  2. 缓冲区满的时候,生产者会进入休眠状态,当下次消费者开始消耗缓冲区数据时,生产者才会被唤醒,开始往缓冲区中添加数据;当缓冲区空的时候,消费者也会进入休眠状态,直到生产者往缓冲区添加数据时才会被唤醒。
  3. 生产者和消费者之间通过队列进行通信和数据传递,使得它们可以独立进行操作

生产者消费者模型优点

  1. 解耦生产者和消费者:生产者和消费者之间通过队列进行通信和数据传递,使得它们可以独立进行操作。这种解耦 提高了代码的灵活性和可维护性,因为你可以更容易地修改或替换生产者和消费者的实现而无需影响其他部分。
  2. 提高系统的响应性和吞吐量:生产者和消费者可以并发地工作,生产者不必等待消费者完成处理才能继续生产,消费者也不必等待生产者生成新的数据才能继续消费。这可以提高系统的响应性和吞吐量,尤其是在处理大量数据时。
  3. 平衡生产和消费速度:生产者消费者模型可以帮助平衡生产和消费的速度。当生产者的速度快于消费者时,数据会积累在队列中,直到消费者可以处理它们。相反,当消费者的速度快于生产者时,队列中的数据会减少,直到有新的数据生成。
  4. 简化并发编程:生产者消费者模型提供了一种结构化的并发编程方式,通过使用队列来处理数据传递和同步,可以避免一些常见的并发编程错误,如竞态条件、死锁等。这使得并发编程更容易理解、调试和维护。
  5. 支持多个生产者和消费者:生产者消费者模型可以很容易地扩展以支持多个生产者和消费者。只需使用一个共享的队列来传递数据,多个生产者可以向队列中添加数据,多个消费者可以从队列中取出数据,而无需修改原有的逻辑。

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

在这里插入图片描述
其中的BlockQueue就是生产者消费者模型当中的交易场所,我们可以用C++STL库当中的queue进行实现。

  • blockingqueue.hpp头文件,定义并实现了了阻塞队列的基本功能
#pragma once

#include <iostream>
#include <string>
#include <queue>
#include <pthread.h>

const static int defaultCap = 5;

template <typename T>
class BlockQueue
{
private:
    bool IsFull()
    {
        return _block_queue.size() == _max_cap;
    }

    bool IsEmpty()
    {
        return _block_queue.empty();
    }

public:
    BlockQueue(int cap = defaultCap) : _max_cap(cap)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_p_cond, nullptr);
        pthread_cond_init(&_c_cond, nullptr);
    }

    void Pop(T *out)
    {
        pthread_mutex_lock(&_mutex);
        while (IsEmpty())
        {
            // 阻塞等待
            pthread_cond_wait(&_c_cond, &_mutex);
        }

        *out = _block_queue.front();
        _block_queue.pop();

        pthread_mutex_unlock(&_mutex);

        // 唤醒生产者
        pthread_cond_signal(&_p_cond);
    }

    void Equeue(const T &in)
    {
        pthread_mutex_lock(&_mutex);

        while (IsFull())
        {
            // 等待
            pthread_cond_wait(&_p_cond, &_mutex);
        }

        _block_queue.push(in);
        pthread_mutex_unlock(&_mutex);

        // 唤醒消费者
        pthread_cond_signal(&_c_cond);
    }

    ~BlockQueue()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_p_cond);
        pthread_cond_destroy(&_c_cond);
    }

private:
    std::queue<T> _block_queue; // 临界资源
    int _max_cap;               // 最大容量
    pthread_mutex_t _mutex;     // 锁
    pthread_cond_t _p_cond;     // 生产者条件变量
    pthread_cond_t _c_cond;     // 消费者条件变量
};

BlockQueue 类实现了一个线程安全阻塞队列,用于生产者-消费者模型。
通过互斥量和条件变量来管理对共享队列的访问,确保在多线程环境中数据的一致性和安全性。
构造函数初始化最大容量和相关的同步机制,IsFull()IsEmpty() 方法用于检查队列状态。生产者通过 Equeue() 方法向队列添加数据,若队列满则等待;消费者通过 Pop() 方法从队列中取出数据,若队列空则等待。


此处分别使用同一把锁和两个条件变量来实现有效的同步,避免了资源的竞争和死锁。


生产者操作 (Enqueue)

  1. 生产者首先使用 pthread_mutex_lock() 获取互斥量 _mutex,保护对 _block_queue 的访问。
  2. 进入一个 while 循环,调用 IsFull() 检查队列是否已满。如果满了,生产者进入等待状态,调用 pthread_cond_wait() 函数,传入 _p_cond_mutex 参数。这个函数会自动释放 _mutex,并阻塞在 _p_cond 条件变量上。
  3. 当队列有空间时(不满),生产者使用 _block_queuepush() 成员函数将数据in推入队列。
  4. 使用 pthread_mutex_unlock() 释放互斥量 _mutex,允许其他线程访问队列。
  5. 调用 pthread_cond_signal() 函数,唤醒一个等待在_c_cond条件变量上的消费者线程,通知它可以从队列中取数据了。

消费者操作 (Dequeue)

  1. 消费者使用 pthread_mutex_lock() 获取互斥量 _mutex
  2. 进入一个 while 循环,调用 IsEmpty() 检查队列是否为空。如果为空,消费者进入等待状态,调用 pthread_cond_wait() 函数,传入 _c_cond _mutex 参数。这个函数会自动释放 _mutex,并阻塞在_c_cond条件变量上。
  3. 当队列有数据时,消费者使用 _block_queuefront()成员函数获取队列头的数据,并存储在 out 指针指向的位置。然后调用 pop() 成员函数从队列中移除这个数据。
  4. 使用 pthread_mutex_unlock() 释放互斥量 _mutex
  5. 调用 pthread_cond_signal() 函数,唤醒一个等待在_p_cond条件变量上的生产者线程,通知它可以生产数据了。
  • main.cpp文件,定义了生产者线程和消费者线程的执行逻辑
#include <iostream>
#include <unistd.h>
#include "BlockQueue.hpp"
#include <ctime>

void *Producer(void *args)
{
    BlockQueue<int> *bq = static_cast<BlockQueue<int> *>(args);
    while (true)
    {
        int val = rand() % 10 + 1;
        bq->Equeue(val);
        std::cout << "Producer send a num -> " << val << std::endl;
        // sleep(1);
    }
    return nullptr;
}


void *Consumer(void *args)
{
    BlockQueue<int> *bq = static_cast<BlockQueue<int> *>(args);
    while (true)
    {
        sleep(1);
        int val = 0;
        bq->Pop(&val);
        std::cout << "Consumer get a num -> " << val << std::endl;
        sleep(1);
    }
}

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

    pthread_join(t1, nullptr);
    pthread_join(t2, nullptr);

    return 0;
}
  • 结果:
    在这里插入图片描述

通过模板也可以将一个Task类传入队列中,如下修改:

  • Task.hpp 定义了Task类,用于传入队列:
#pragma once
#include <iostream>
#include <string>

class Task
{
public:
    Task(int x = 0, int y = 0) : _x(x), _y(y), _result(0) // 初始化 _result
    {
    }

    ~Task()
    {
    }

    void Execute() // 更正拼写
    {
        _result = _x + _y; // 计算结果
    }

    void Debug()
    {
        std::cout << std::to_string(_x) + " + " + std::to_string(_y) + " = " + " ? " << std::endl;
    }

    void PrintResult() // 新增输出结果的方法
    {
        std::cout << _x << " + " << _y << " = " << _result << std::endl;
    }

private:
    int _x;
    int _y;
    int _result; // 结果变量
};
  • main.cc
#include <iostream>
#include <unistd.h>
#include "BlockQueue.hpp"
#include <ctime>
#include "Task.hpp"

void *Producer(void *args)
{
    BlockQueue<Task> *bq = static_cast<BlockQueue<Task> *>(args);
    while (true)
    {
        sleep(1);
       int x = rand() % 10 + 1;
       usleep(10000);
       int y = rand() % 10 + 1;
       Task t(x,y);
        bq->Equeue(t);
        t.Debug();
    }
    return nullptr;
}


void *Consumer(void *args)
{
    BlockQueue<Task> *bq = static_cast<BlockQueue<Task> *>(args);
    while (true)
    {
        sleep(1);
        Task t;
        bq->Pop(&t);
        t.Execute(); // 计算结果
        t.PrintResult(); // 输出结果
    }
}

int main()
{

    

    BlockQueue<Task> *bq = new BlockQueue<Task>();
    srand(time(nullptr));
    
    pthread_t t1, t2;
    pthread_create(&t1, nullptr, Producer, bq);
    pthread_create(&t2, nullptr, Consumer, bq);

    pthread_join(t1, nullptr);
    pthread_join(t2, nullptr);

    return 0;
}
  • 结果:
    在这里插入图片描述

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

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

相关文章

Python 3.11 从入门到实战1(环境准备)

本篇文章是python3.11的学习开篇&#xff0c;我们的目标是&#xff1a;通过这一套资料学习下来&#xff0c;获得python基础学习与实例、实践相结合&#xff0c;使我们完全掌握python。并做到独立完成项目开发的能力。 今天的谈论的比较简单&#xff0c;也是后续学习的基础。pyt…

深入MySQL

MySQL逻辑架构 MySQL逻辑架构整体分为三层&#xff0c;顶层客户端并非MySQL独有&#xff0c;如&#xff1a;连接处理、授权认证、安全等功能都在客户端层。 核心服务层&#xff0c;MySQL大多数核心服务都在这一层&#xff0c;包括查询解析、分析、优化、缓存、内置函数等。所有…

Python中csv文件的操作5

在《Python中csv文件的操作1》中提到&#xff0c;可以通过read()和write()方法读取和写入csv文件中的所有内容。除了上述的两个方法外&#xff0c;还可以通过readline()、readlines()、writelines()实现csv文件的读写操作。 1 readline()方法 readline()方法每次只读取文件的…

入行「游戏策划」,该从何处下手?

想知道策划岗位该怎么入行可点击蓝链 相比较起以技术为最重要评判标准的开发岗&#xff0c; 「游戏策划」这一岗位在非业界人士的眼中 一直都是一个风评方差很大的岗位。 有人说策划岗又轻松又威风&#xff0c; 只需要输出想法&#xff0c;落地都交给开发&#xff0c; 干…

xss-labs 11-15关通关攻略

第11关 一.进入11关 二.进行抓包 在抓到的数据包中加入Referer:"οnclick"alert(1)"type"text 第12关 进入第12关发现没有注入点&#xff0c;进入源代码看哪里可以注入发现 二.在ua头注入 Referer:"οnclick"alert(1)"type"text…

【鸿蒙样式初探】多个组件如何共用同一样式

最近开发鸿蒙&#xff0c;刚接触难免二和尚摸不着头脑&#xff0c;尤其是样式...... 背景 在做银行卡显示的一个小需求时&#xff1a; 每个Text都需要设置fontColor:#FFFFFF" 想着是否可以简单点 解决历程 思路一&#xff1a;&#xff08;拒绝) 使用Styles 提取封装公…

matlab仿真 信道编码和交织(下)

&#xff08;内容源自详解MATLAB&#xff0f;SIMULINK 通信系统建模与仿真 刘学勇编著第八章内容&#xff0c;有兴趣的读者请阅读原书&#xff09; ​ ​ ​ clear alln3;k2;%A(3,2)循环码N10000;%消息比特的行数msgrandi([0 1],N,k);%消息比特一共N*k行polcyclpoly(n,k);…

C++ TinyWebServer项目总结(12. 高性能I/O框架库Libevent)

Linux服务器程序必须处理三类事件&#xff08;I/O、信号和定时事件&#xff09;&#xff0c;在处理这三类事件时需要考虑以下问题&#xff1a; 统一事件源。统一处理这三类事件既能使代码简单易懂&#xff0c;又能避免一些潜在的逻辑错误。实现统一事件源的一般方法&#xff1…

如何用Java SpringBoot+Vue搭建花开富贵花园管理系统

&#x1f393; 作者&#xff1a;计算机毕设小月哥 | 软件开发专家 &#x1f5a5;️ 简介&#xff1a;8年计算机软件程序开发经验。精通Java、Python、微信小程序、安卓、大数据、PHP、.NET|C#、Golang等技术栈。 &#x1f6e0;️ 专业服务 &#x1f6e0;️ 需求定制化开发源码提…

推荐一篇 学习SQL 的文章

学习 java&#xff0c;当然避不开数据库的知识&#xff0c;个人认为好学好理解的一篇文章&#xff0c;推荐给大家 SQL语法基础知识总结 | JavaGuide「Java学习 面试指南」一份涵盖大部分 Java 程序员所需要掌握的核心知识。准备 Java 面试&#xff0c;首选 JavaGuide&#xf…

智能优化算法-鹈鹕优化算法(POA)(附源码)

目录 1.内容介绍 2.部分代码 3.实验结果 4.内容获取 1.内容介绍 鹈鹕优化算法 (Pelican Optimization Algorithm, POA) 是一种基于群体智能的元启发式优化算法&#xff0c;它模拟了鹈鹕的捕食行为和社会交互特性&#xff0c;用于解决复杂的优化问题。 POA的工作机制主要包括…

单元测试、系统测试和集成测试知识详解

&#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 一、单元测试的概念 单元测试是对软件基本组成单元进行的测试&#xff0c;如函数或一个类的方法。当然这里的基本单元不仅仅指的是一个函数或者方法&#xff…

FlagEval 8月榜 | 文生视频大模型主观评测结果揭晓,新增6款新发布模型

近日&#xff0c;智源研究院联合中国传媒大学发布文生视频大模型主观评测榜单&#xff0c;在今年5月对Sora、Runway Gen-2、PixVerse V1、Pika 1.0、VideoCrafter-V2、Show-1、Open-Sora 1.0七个模型性能表现评测结果的基础之上&#xff0c;不仅对部分模型的升级版本进行了对比…

C++初学(16)

16.1、if语句 当C程序必须决定是否执行某个操作时&#xff0c;通常使用if语句来实现选择。if有两种格式&#xff1a;if和if else。 if语句的语法与while相似&#xff1a; if(text-condition)statement 如果text-condition&#xff08;测试条件&#xff09;为true&#xff0…

iTimes工时管理系统:企业高效管理的得力助手

在当今快节奏的商业环境中&#xff0c;企业面临着越来越多的挑战&#xff0c;其中之一便是如何高效、准确地管理员工工时。工时管理不仅关乎企业的成本控制&#xff0c;还直接影响到项目执行效率、员工满意度以及整体运营水平。因此&#xff0c;选择一款优秀的工时管理系统显得…

【应用层】Tomcat10安装以及对应的VScode插件使用

文章日期是2024年8月26日&#xff0c;Tomcat10为稳定版中最新的&#xff0c;Tomcat11为测试版。 流程&#xff1a;下载Tomcat10-->等待下载时&#xff0c;安装对应的VScode插件-->配置Tomcat10-->配置对应的VScode插件 1、下载Tomcat10 2、安装对应的VScode插件 3…

Codeforce 963

CF 963 B 模拟加贪心 偶数个数C 模拟前缀和 灯能否全亮D 二分DP 中位数尽可能大F1 模拟镜像 题目链接 B 模拟加贪心 偶数个数 考点&#xff1a;贪心 思路&#xff1a;除了全是偶数的情况&#xff0c;其他的情况都需要将偶数转换为奇数。最少的操作步数是偶数个数&#xff0c;…

IOS 15 实现Toast和小菊花Loading提示

本文主要是实现toast和loading两种提示功能&#xff0c;例如&#xff1a;登陆时参数不正确提示&#xff0c;toast提示后会自动隐藏。加载提示&#xff1a;不会自动隐藏&#xff0c;常用于网络请求&#xff0c;上传等。 添加依赖 #提示框架 #https://github.com/jdg/MBProgress…

20240828 每日AI必读资讯

8岁女孩玩转AI编程&#xff0c;45分钟打造聊天机器人&#xff0c;Karpathy都看呆了 - 新晋顶流AI代码编辑器——Cursor&#xff0c;已经进化到了“0手工代码”阶段。 - 提供了多个AI模型&#xff0c;包括GPT-4、GPT-4o和Claude 3.5 Sonnet等&#xff0c;可以通过跟大模型聊天…