【Linux】多线程——生产者和消费者模型

news2024/9/28 1:23:56

目录

1 生活中的例子

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

3 生产者和消费者模型的特点

优点

4 如何理解生产消费模型提高了效率?

5 基于BlockingQueue(阻塞队列)的生产者消费者模型

C++ queue模拟阻塞队列的生产消费模型 


1 生活中的例子

存在多个消费者,消费者对于商品的一次消费是小量的而且时间是不确定的,供货商一次生产的商品是大量的且时间是确定的(在工人的上班时间才能操作机械生产)。消费者没有能力也没有必要一次性购入多个相同的商品,供货商多次生产量小商品的成本比一次生产大量商品的成本高;消费者一般都在城区里生活,供货商的厂房一般都远离城区,所以消费者不方便到郊区购买产品。为了解决以上种种苦难,中间商——超市就出来了!在城区里面建立超市,对供货商,大批运入大量的产品;对消费者,可以提供多种多样的产品。生产者不需要等消费者消费的时候才生产产品,消费者消费的时候不用等生产者生产产品。通过超市,将生产者和消费者之间进行了解耦合,提高了效率,解决了忙闲不不均的问题!

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

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

3 生产者和消费者模型的特点

首先,交易场所必须先被所有的线程看到(生产者和消费者线程)!注定了交易场所一定是一个会被多线程并发访问的公共区域,注定了多线程一定要保护共享资源的安全,注定了,在这种情况下,程序员一定要自己维护线程互斥与同步的关系!

问题:如何维护线程的互斥和同步的关系?--通过生产消费模型中,各个角色之间的关系!

优点

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

4 如何理解生产消费模型提高了效率?

首先明确的是,高效并不体现在从仓库中放入数据和提取数据中,因为这是串发的!

其次,我们要明确的是,生产者生产数据的过程可能漫长且独立的,消费者消费(处理)数据的时候可能漫长且独立的。

所以,提高的效率就体现在产生数据和处理数据上,因为可以多线程产出的数据,多线程处理数据,并发进行。生产数据和处理数据之间不需要相互等待,直接从仓库中存拿即可。

5 基于BlockingQueue(阻塞队列)的生产者消费者模型

BlockingQueue

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

C++ queue模拟阻塞队列的生产消费模型 


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

const int N=5;

template<class T>
class BlockQueue
{
private:
    void lock()
    {
        pthread_mutex_lock(&_mutex);
    }
    void unlock()
    {
        pthread_mutex_unlock(&_mutex);
    }
    bool isFull()
    {
        return _q.size()==_capacity;
    }
    bool isEmpty()
    {
        return _q.empty();
    }
    
    void pthreadWait(pthread_cond_t& cond)
    {
        pthread_cond_wait(&cond,&_mutex);
    }

    void pthreadWakeUp(pthread_cond_t& cond)
    {
        pthread_cond_signal(&cond);
    }

public:
    BlockQueue(const int num=N):_capacity(num)
    {
        pthread_mutex_init(&_mutex,nullptr);
        pthread_cond_init(&_comsumer,nullptr);
        pthread_cond_init(&_producer,nullptr);
    }

    ~BlockQueue()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_comsumer);
        pthread_cond_destroy(&_producer);
    }

    void push(const T& task)
    {
        lock();
        while(isFull())//保证任何时候,要生产时仓库有空余
        {
            pthreadWait(_producer);
        }
        //向仓库运数据
        _q.push(task);
        //有了产品,唤醒消费者消费
        pthreadWakeUp(_comsumer);
        unlock();
    }

    void pop(T* out)
    {
        lock();
        while(isEmpty())
        {
            pthreadWait(_comsumer);
        }
        //仓库有产品,开始消费
        *out=_q.front();
        _q.pop();
        //消费完产品,仓库有空余,唤醒生产者生产
        pthreadWakeUp(_producer);
        unlock();
    }

private:
    std::queue<T> _q;//队列存储数据,被多线程看到,需要被保护
    int _capacity;
    pthread_mutex_t _mutex;//保护队列,一个锁,一个共享资源,就可以保证互斥关系的成立
    //两个条件变量,保证消费者和生产者之间的同步关系
    pthread_cond_t _comsumer;//控制消费速度
    pthread_cond_t _producer;//控制生产速度
};

main.cc测试代码

#include<iostream>
#include"blockQueue.hpp"
#include"task.hpp"
using namespace std;

#include<ctime>

#include<pthread.h>
#include<unistd.h>

std::string ops="+-*/";

void* consumerRountine(void* args)
{
    BlockQueue<Task>* bq=static_cast<BlockQueue<Task>*>(args);
    while(true)
    {
        //1. 从队列中拿取数据
        Task t;
        bq->pop(&t);
        //2. 处理数据,完成消费工作
        t();
        cout<<pthread_self()<<" | "<<t.formatArg()<<t.formatRes()<<endl;
    }
    return nullptr;
}
void* producerRountine(void* args)
{
    BlockQueue<Task>* bq=static_cast<BlockQueue<Task>*>(args);
    while(true)
    {
        //1. 生产数据
        int x=rand()%10;
        int y=rand()%10;
        char op=ops[rand()%ops.size()];
        Task t(x,y,op);
        //2. 将数据推送到blockqueue的结构中,完成生产工作
        bq->push(t);
        cout<<pthread_self()<<" | "<<t.formatArg()<<"?"<<endl;
    }
    return nullptr;
}

int main()
{
    srand((uint64_t)time(nullptr)^getpid());
    pthread_t c[3],p[2];
    BlockQueue<Task> bq;
    pthread_create(&c[0],nullptr,consumerRountine,&bq);
    pthread_create(&c[1],nullptr,consumerRountine,&bq);
    pthread_create(&c[2],nullptr,consumerRountine,&bq);
    pthread_create(&p[0],nullptr,producerRountine,&bq);
    pthread_create(&p[1],nullptr,producerRountine,&bq);

    pthread_join(c[0],nullptr);
    pthread_join(c[1],nullptr);
    pthread_join(c[2],nullptr);
    pthread_join(p[0],nullptr);
    pthread_join(p[1],nullptr);

    return 0;
}


// void* consumerRountine(void* args)
// {
//     BlockQueue<int>* bq=static_cast<BlockQueue<int>*>(args);
//     while(true)
//     {
//         //1. 从队列中拿取数据
//         int data=0;
//         bq->pop(&data);
//         //2. 处理数据,完成消费工作
//         cout<<pthread_self()<<"| consumerRountine done, "<<"data: "<<data<<endl;
//     }
//     return nullptr;
// }
// void* producerRountine(void* args)
// {
//     BlockQueue<int>* bq=static_cast<BlockQueue<int>*>(args);
//     while(true)
//     {
//         sleep(1);
//         //1. 生产数据
//         int data=rand()%10;
//         //2. 将数据推送到blockqueue的结构中,完成生产工作
//         bq->push(data);
//         cout<<pthread_self()<<"| push data,"<<"data:"<<data<<endl;
//     }
//     return nullptr;
// }

// int main()
// {
//     srand((uint64_t)time(nullptr)^getpid());
//     pthread_t c,p;
//     BlockQueue<int> bq;
//     pthread_create(&c,nullptr,consumerRountine,&bq);
//     pthread_create(&p,nullptr,producerRountine,&bq);

//     pthread_join(c,nullptr);
//     pthread_join(p,nullptr);

//     return 0;
// }

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

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

相关文章

pve安装ikuai并设置,同时把pve的网络连接到ikuai虚拟机

目录 前因 前置条件 安装ikuai 进入ikuai的后台 配置lan口&#xff0c;以及wan口 配置lan口桥接 按实际情况来设置了 单拨&#xff08;PPOE拨号&#xff09; 多拨(内外网设置点击基于物理网卡的混合模式) 后续步骤 pve连接虚拟机ikuai的网络以及其他虚拟机连接ikuai的网…

QT中日期和时间类

QT中日期和时间类 QDateQTimeQDateTime QDate QDate类可以封装日期信息也可以通过这个类得到日期相关的信息, 包括:年, 月, 日。 // 构造函数 QDate::QDate(); QDate::QDate(int y, int m, int d);// 公共成员函数 // 重新设置日期对象中的日期 bool QDate::setDate(int year…

【数据结构与算法】整合一

GitHub同步更新&#xff08;已分类&#xff09;&#xff1a;Data_Structure_And_Algorithm-Review 公众号&#xff1a;URLeisure 的复习仓库 公众号二维码见文末 以下是本篇文章正文内容&#xff0c;下面案例可供参考。 吐血整理数据结构合集一&#xff1a; 整理了之前发的文…

SpringBoot集成Thymeleaf

Spring Boot 集成 Thymeleaf 模板引擎 1、Thymeleaf 介绍 Thymeleaf 是适用于 Web 和独立环境的现代服务器端 Java 模板引擎。 Thymeleaf 的主要目标是为开发工作流程带来优雅的自然模板&#xff0c;既可以在浏览器中正确显示的 HTML&#xff0c;也可以用作静态原型&#xf…

华为云NFS使用API删除大文件目录

最近在使用华为云SFS时&#xff0c;如果一个目录存储文件数超过100W&#xff0c;执行 “rm -rf path”时&#xff0c;存在删不动的情况&#xff0c;可以使用华为云API接口&#xff0c;执行异步删除。 华为官网&#xff1a; 删除文件系统目录_弹性文件服务 SFS_API参考_SFS Tu…

八方锦程15周年巡讲重磅启动,上海首场巡讲圆满举行

7月27日,八方锦程15周年巡讲——“与行业大咖同行 打造卓越人力资源官”活动在上海圆满举行! 作为八方锦程15周年巡讲的第一站,这一时刻,近200位与会人力资源从业者共同见证。 活动由八方锦程人才研究院副院长Leon主持并致开幕词。他向与会人员分享了举办该系列活动的初心,以八…

Mybatis源码-三种SqlSession的区别

三个SqlSession DefaultSqlSession与SqlSessionManager 与SqlSessionTemplate 是我常见的3种sqlsesion 从类图可以看出他们三个都实现了了SqlSession&#xff0c;也就是他们都可以表示一个会话。与其他不同的是SqlSessionManager实现了SqlSessionFactory 这三种sqlsession的区…

Windows 找不到文件‘chrome‘。请确定文件名是否正确后,再试一次

爱像时间&#xff0c;永恒不变而又短暂&#xff1b;爱像流水&#xff0c;浩瀚壮阔却又普普通通。 Windows 找不到文件chrome。请确定文件名是否正确后&#xff0c;再试一次 如果 Windows 提示找不到文件 "chrome"&#xff0c;可能是由于以下几种原因导致的&#xff1…

vmware中windows操作系统虚拟机安装

1.win10中安装 1.1 虚拟机向导 文件-新建虚拟机 典型-下一步 稍后安装操作系统-下一步 window10 64x -下一步 修改虚拟机名称及位置-下一步 默认60g,至少大于40g-将虚拟磁盘拆分成多个文件夹-下一步 点击完成 1.2 编辑虚拟机设置 移除打印机 设置虚拟机&#xff0c;加入iso映…

DAY4,Qt(事件处理机制的使用,Qt中实现服务器的原理)

1.Qt中实现服务器的原理&#xff1b; ---chatser.h---头文件 #ifndef CHATSER_H #define CHATSER_H#include <QWidget> #include <QTcpServer> //服务器类 #include <QTcpSocket> //套接字类 #include <QMessageBox> //消息对话类 #include <Q…

机器学习深度学习——模型选择、欠拟合和过拟合

&#x1f468;‍&#x1f393;作者简介&#xff1a;一位即将上大四&#xff0c;正专攻机器学习的保研er &#x1f30c;上期文章&#xff1a;机器学习&&深度学习——多层感知机的简洁实现 &#x1f4da;订阅专栏&#xff1a;机器学习&&深度学习 希望文章对你们有…

HHDESK便捷功能介绍三

1 连接便捷显示 工作中&#xff0c;往往需要设置很多资源连接。而过多的连接设&#xff0c;往往很容易混淆。 在HHDESK中&#xff0c;当鼠标点击连接时&#xff0c;会在下方显示本连接的参数&#xff0c;方便用户查看。 2 日志查看 实际工作中&#xff0c;查看日志是一件很…

干货 | 5个经典的模拟电路解析,电子人必看!

干货 | 5个经典的模拟电路解析&#xff0c;电子人必看&#xff01; 作为一个电子人&#xff0c;我们平时需要和不同的电路接触&#xff0c;但有一些电路图是经典的&#xff0c;值得我们永远记住。一、自举电路 此电路用在各种ADC之前的采样电路&#xff0c;可以让ADC实现轨到轨…

lc154.寻找旋转排序数组中的最小值

最小元素的位置以旋转次数为索引的位置&#xff0c;但是没有告诉旋转次数&#xff0c;换一种思路 当遇到arr[index] > arr[index1]时&#xff0c;index1为最小元素的位置。首位位置独立比较。但是这种方法还是遍历数组 观察两组数的中间值与首尾的值&#xff0c;又由于数组…

变现:利用 chatgpt + midjourney 制作微信表情包

1、利用gpt生成提示词&#xff0c;当然也可以直接翻译 生成基础提示词&#xff0c; 比如&#xff1a; an anime image with a white kawaii character in it, in the style of light green and brown, minimalist detail, animated gifs, cranberrycore, 1860–1969, babyco…

企业做数字化转型,请先避开这5个坑!

前言&#xff1a; “多种薪酬结构并存”的中国企业&#xff0c;在面临线下算薪、线上算税、表格数据整理易出错的问题时&#xff0c;有没有高效的解决办法&#xff1f;在考勤记录需要结合纸质版假条核对时&#xff0c;怎样减少人事的工作量&#xff1f;企业在积累了海量业务数…

【探讨】Java POI 处理 Excel 中的名称管理器

前言 最近遇到了一些导表的问题。原本的导表工具导不了使用名称管理器的Excel。 首先我们有两个Sheet。B1用的是名称管理器中的AAA, 而B2用的对应的公式。 第二个sheet&#xff0c;名为Test2: 这是一段简化的代码&#xff1a; public class Main {public static void mai…

Rust ESP32C3开发

Rust ESP32C3开发 系统开发逐步使用Rust语言&#xff0c;在嵌入式领域Rust也逐步完善&#xff0c;本着学习Rust和ESP32的目的&#xff0c;搭建了ESP32C3的环境&#xff0c;过程中遇到了不少问题&#xff0c;予以记录。 ESP-IDF开发ESP32 这一部分可跳过&#xff0c;是使用C开…

浏览器中的Markdown编辑器StackEdit

目前博客的 Pageviews 大约是之前的 10%&#xff0c;而 Uniques 则大约是 15% 左右。看来很多同学已经彻底迷路了 大家可以关注CSDN&#xff0c;地址&#xff1a; https://blog.csdn.net/wbsu2004 微信公众号也可以关注起来 什么是 StackEdit &#xff1f; StackEdit 是基于 P…

pyspark 笔记 cast 转换列的类型

1 不借助 pyspark.sql.types from pyspark.sql.functions import coldata [("Alice", "28"), ("Bob", "22"), ("Charlie", "30")] columns ["name", "age_str"] df spark.createDataFram…