Linux之生产消费者模型

(。・∀・)ノ゙嗨!你好这里是ky233的主页:这里是ky233的主页,欢迎光临~icon-default.png?t=N7T8https://blog.csdn.net/ky233?type=blog

点个关注不迷路⌯'▾'⌯

我们在条件满足的时候,唤醒指定的线程,我怎么知道条件是否满足呢?

一、概念

1、例子说明,生产消费的基本组成概念

我们在超市买东西,都知道超市的东西从供货商里来,而我们是消费者,超市是交易场所!

我们为什么不跳过超市直接在供货商里买呢?

这是因为供货商不卖哈哈哈,那为什么我们可以在超市买呢?因为超市把所有的消费者集结在了一起,集中在生产者里面批发,这里面的意义在于提高效率!、

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

  • 有三种关系:生产者与生产者,消费者与消费者,生产者与消费者
  • 有两种角色:生产者与消费者
  • 有一种场所:超市
  • 我们只有处理好上面的三种规则才能避免混乱

生产者与生产者之间是竞争关系,说白了就是我要这个资源,我不想给你,也是互斥关系

消费者与消费者之间是竞争关系,这个本质上也是竞争和互斥关系,尤其是在资源有限的情况下

生产者与消费者的关系是互斥或者同步的关系!东西没了让生产,消费者等待,东西多了则反之!

所以生产消费模型要遵守以上原则!

2、用基本工程师思维,重新理解生产消费

生产者和消费者对应的就是我们的线程来承担,也就是给线程进行角色化,而交易场所呢?通常是某种数据结构表示的缓冲区!

也就是说我们的一部分线程生产对应的数据,另一部分线程消费对应的数据做处理,缓冲区存放这些数据!

那么超市里有没有东西谁最清楚呢?答案是消费者最清楚,

所以条件满足时,我们在唤醒指定的线程,我们怎么直到条件是否满足呢?这是因为我们的生产者生产完成之后就可以通知消费者来消费,消费者把数据拿走,也会通知我们的生产者

3.补充点

1.如果只有一个消费者和一个生产者,那是不是只需要维护生产和数据的安全和同步这样的策略呢?是不是就不要维护生产者和生产者,消费者与消费者之间的互斥与同步了!

答案是:是的!

2.生产和消费的过程是不是把数据放到仓库当中,另一个消费者把他拿走呢?

是的,但不仅仅如此

生产者生产的数据是从哪里来的呢?消费者如何使用发送过来的数据呢?

这两点现在我们都不清楚,但是我们知道要完成就需要花时间!!

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

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

生产者消费模型主要的节省效率是在我们的生产者和消费者之间的并发动,因为处理数据和生产数据都需要花费时间!在生产的时候我们消费者可以继续除非处理我们拿到的数据而不用一直拿数据,这样就形成了并发!并发是指生产者与生产者,消费者与消费者之间的并发!!

1.BlockQueue.hpp 

#pragma once

#include <iostream>
#include <queue>
#include <mutex>
#include <pthread.h>
#include <unistd.h>
#include "lockGuard.hpp"

const int gDefaultCap = 5;

using namespace std;
template <class T>

class BlockQueue
{
private:
    bool isQueueEmpty()
    {
        return bq_.size() == 0;
    }

    bool isQueueFull()
    {
        return bq_.size() == capacity_;
    }

public:
    BlockQueue(int capacity = gDefaultCap) : capacity_(capacity)
    {
        pthread_mutex_init(&mtx_, nullptr);
        pthread_cond_init(&Empty_, nullptr);
        pthread_cond_init(&Full_, nullptr);
    }

    void push(const T &in)
    {
        // pthread_mutex_lock(&mtx_);
        // // 1.先检测当前的临界资源是否为满足访问的条件
        // // 在临界区中进行等待,所以也要释放锁,wait第二个参数传入的锁会被自动解锁
        // // 被唤醒时也会在临界资源里唤醒,从哪里阻塞也就会在哪里唤醒,并且也会自动的加锁
        // //可能会存在伪唤醒状态所以要用while再次判断下
        // while (isQueueFull())
        // {
        //     pthread_cond_wait(&Full_, &mtx_);
        // }
        // //走到这里就可以确定确实是就绪的
        // // 访问临界资源
        // bq_.push(in);
        // // 生产成功唤醒消费者

        // pthread_cond_signal(&Empty_);

        // pthread_mutex_unlock(&mtx_);

        lockGuard lockguard(&mtx_);
        while (isQueueFull())
        {
            pthread_cond_wait(&Full_, &mtx_);
        }
        // 访问临界资源
        bq_.push(in);
        // 生产成功唤醒消费者

        pthread_cond_signal(&Empty_);
        // 会自动调用析构,等价上面的写法!
    }

    void pop(T *out)
    {
        lockGuard lockguard(&mtx_);

        // pthread_mutex_lock(&mtx_);
        while (isQueueEmpty())
        {
            pthread_cond_wait(&Empty_, &mtx_);
        }
        *out = bq_.front();
        bq_.pop();

        // pthread_mutex_unlock(&mtx_);
        //  拿走之后唤醒生产者
        pthread_cond_signal(&Full_);
    }

    ~BlockQueue()
    {
        pthread_mutex_destroy(&mtx_);
        pthread_cond_destroy(&Empty_);
        pthread_cond_destroy(&Full_);
    }

private:
    queue<T> bq_;
    int capacity_;         // 容量上限
    pthread_mutex_t mtx_;  // 通过互斥锁保证线程安全
    pthread_cond_t Empty_; // 来表述阻塞队列是否空的条件
    pthread_cond_t Full_;  // 来表述阻塞队列是否满了的条件
};

2.ConProd.cc

#include "BlockQueue.hpp"
#include "Task.hpp"
#include <ctime>

int myadd(int x, int y)
{
    return x + y;
}
void *consumer(void *args)
{
    BlockQueue<Task> *bqueue = (BlockQueue<Task> *)args;
    while (1)
    {
        // 获取任务
        Task t;
        bqueue->pop(&t);
        // 完成任务
        cout << pthread_self()<<" 消费者" << t.x_ << "+" << t.y_ << "=" << t() << endl;
        sleep(1);
    }
    return nullptr;
}

void *productor(void *args)
{
    BlockQueue<Task> *bqueue = (BlockQueue<Task> *)args;

    while (1)
    {
        // 制作任务
        int x = rand()%10 + 1;
        usleep(rand()%1000);
        int y = rand()%5 + 1;
        // int x,y;
        // cout<<"请输入x:";
        // cin>>x;
        // cout<<"请输入y";
        // cin>>y;
        Task t(x, y, myadd);
        // 生产任务
        bqueue->push(t);
        // 输出信息
        cout << pthread_self()<<" 生产者" << t.x_ << "+" << t.y_ << "=?" << endl;

    }
    return nullptr;
}

int main()
{
    srand((uint64_t)time(nullptr) ^ getpid() ^ 0x25415);
    BlockQueue<Task> *bqueue = new BlockQueue<Task>();

    pthread_t c[2], p[2];
    pthread_create(c, nullptr, consumer, bqueue);
    pthread_create(c+1, nullptr, consumer, bqueue);
    pthread_create(p, nullptr, consumer, bqueue);
    pthread_create(p+1, nullptr, productor, bqueue);

    pthread_join(c[0], nullptr);
    pthread_join(c[1], nullptr);
    pthread_join(p[0], nullptr);
    pthread_join(p[1], nullptr);
    delete bqueue;
    return 0;
}

3.lockGuard.hpp

#pragma once

#include <iostream>
#include <pthread.h>
using namespace std;

class Mutex
{
public:
    Mutex(pthread_mutex_t *mtx) : pmtx_(mtx)
    {
    }

    void lock()
    {
        cout << "加锁" << endl;
        pthread_mutex_lock(pmtx_);
    }

    void unlock()
    {
        cout << "解锁" << endl;
        pthread_mutex_unlock(pmtx_);
    }

    ~Mutex()
    {
    }

private:
    pthread_mutex_t *pmtx_;
};

// RAII的枷锁风格
class lockGuard
{
public:
    lockGuard(pthread_mutex_t *mtx) : mtx_(mtx)
    {
        mtx_.lock();
    }

    ~lockGuard()
    {
        mtx_.unlock();
    }

private:
    Mutex mtx_;
};

 4.Task.hpp

#pragma once

#include <iostream>
#include <functional>
using namespace std;
    typedef 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_;
};

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

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

相关文章

超越Chain-of-Thought LLM 推理

原文地址&#xff1a;Beyond Chain-of-Thought LLM Reasoning 2024 年 2 月 13 日 介绍 最近的一项研究解决了需要增强大型语言模型 (LLM) 的推理能力&#xff0c;超越直接推理 (Direct Reasoning&#xff0c;DR) 框架&#xff0c;例如思想链和自我一致性&#xff0c;这些框架可…

分割模型TransNetR的pytorch代码学习笔记

这个模型在U-net的基础上融合了Transformer模块和残差网络的原理。 论文地址&#xff1a;https://arxiv.org/pdf/2303.07428.pdf 具体的网络结构如下&#xff1a; 网络的原理还是比较简单的&#xff0c; 编码分支用的是预训练的resnet模块&#xff0c;解码分支则重新设计了。…

HTML入门:属性

你好&#xff0c;我是云桃桃。今天来聊一聊 HTML 属性写法和特点。 HTML 属性是用于向 HTML 标签&#xff08;也叫 HTML 元素&#xff09;提供附加信息或配置的特性。 如果说&#xff0c;把HTML 标签比作一个房子&#xff0c;HTML 标签定义了房子的结构和用途&#xff0c;比如…

基于SpringBoot的闲置房屋搜索平台设计与实现

目 录 摘 要 I Abstract II 引 言 1 1相关技术 3 1.1 jQuery技术简介 3 1.2 SpringBoot框架简介 3 1.3 Bootstrap框架简介 4 1.4 ECharts框架简介 4 1.5 百度地图API简介 4 1.6 Ajax技术简介 5 1.7 MySQL数据库简介 5 1.8本章小结 6 2系统分析 7 2.1功能需求 7 2.2非功能需求 …

微软财务GPT Excel Copilot for Finance使用攻略

功能本身不收费&#xff0c;但是这个功能需要微软的商业版office账号才能使用&#xff0c;如果你没有账号&#xff0c;可以直说。 在桌面Excel软件中登录账号后&#xff0c;点击“copilot for finance”按钮&#xff0c;如果没有出现&#xff0c;则点击“加载项”&#xff0c;…

2024 年中国高校大数据挑战赛赛题 D:行业职业技术培训能力评价完整思路以及源代码分享

中国是制造业大国&#xff0c;产业门类齐全&#xff0c;每年需要培养大量的技能娴 熟的技术工人进入工厂。某行业在全国有多所不同类型&#xff08;如国家级、 省级等&#xff09;的职业技术培训学校&#xff0c;进行 5 种技能培训。学员入校时需要 进行统一的技能考核&#xf…

简述epoll实现

所有学习笔记&#xff1a;https://github.com/Dusongg/StudyNotes 文章目录 epoll数据结构的选择&#xff1f;以tcp为例&#xff0c;网络io的可读可写如何判断&#xff1f;epoll如何做到线程安全&#xff1f;LT和ET如何实现&#xff1f;tcp状态和io的读写有哪些关系&#xff1…

文本生成视频:从 Write-a-video到 Sora

2024年2月15日&#xff0c;OpenAI 推出了其最新的文本生成视频模型——Sora。Sora 能够根据用户的指令生成一分钟长度的高质量视频内容。这一创新的发布迅速在社会各界引发了广泛关注与深入讨论。本文将围绕本实验室发表于SIGGRAPH AISA 的 Write-a-video和 Sora 展开&#xff…

CPU设计实战-协处理器访问指令的实现

目录 一 协处理器的作用与功能 1.计数寄存器和比较寄存器 2.Status寄存器 3.Cause寄存器(标号为13) 4.EPC寄存器(标号为14) 5.PRId寄存器(标号为15) 6.Config 寄存器(标号为16)-配置寄存器 二 协处理器的实现 三 协处理器访问指令说明 四 具体实现 1.译码阶段 2.执行…

git命令行提交——github

1. 克隆仓库至本地 git clone 右键paste&#xff08;github仓库地址&#xff09; cd 仓库路径&#xff08;进入到仓库内部准备提交文件等操作&#xff09; 2. 查看main分支 git branch&#xff08;列出本地仓库中的所有分支&#xff09; 3. 创建新分支&#xff08;可省…

Edu18 -- Divide by Three --- 题解

目录 Divide by Three&#xff1a; 题目大意&#xff1a; ​编辑​编辑思路解析&#xff1a; 代码实现&#xff1a; Divide by Three&#xff1a; 题目大意&#xff1a; 思路解析&#xff1a; 一个数字是3的倍数&#xff0c;那么他的数位之和也是3的倍数&#xff0c;所以我…

安信可IDE(AiThinker_IDE)编译ESP8266工程方法

0 工具准备 AiThinker_IDE.exe ESP8266工程源码 1 安信可IDE&#xff08;AiThinker_IDE&#xff09;编译ESP8266工程方法 1.1 解压ESP8266工程文件夹 我们这里使用的是NON-OS_SDK&#xff0c;将NON-OS_SDK中的1_UART文件夹解压到工作目录即可 我这里解压到了桌面&#xff0c…

WiFi模块助力少儿编程:创新学习与实践体验

随着科技的飞速发展&#xff0c;少儿编程已经成为培养孩子们创造力和问题解决能力的重要途径之一。在这个过程中&#xff0c;WiFi模块的应用为少儿编程领域注入了新的活力&#xff0c;使得学习编程不再是单一的代码教学&#xff0c;而是一个充满创新与实践的综合性体验。 物联网…

Redis作为缓存的数据一致性问题

背景 使用Reids作为缓存的原因&#xff1a; 在高并发场景下&#xff0c;传统关系型数据库的并发能力相对比较薄弱&#xff08;QPS不能太大&#xff09;&#xff1b; 使用Redis做一个缓存。让用户请求先打到Redis上而不是直接打到数据库上。 但是如果出现数据更新操作&#xff…

开发指南002-前后端信息交互规范-概述

前后端之间采用restful接口&#xff0c;服务和服务之间使用feign。信息交互遵循如下平台规范&#xff1a; 前端&#xff1a; 建立api目录&#xff0c;按照业务区分建立不同的.js文件&#xff0c;封装对后台的调用操作。其中qlm*.js为平台预制的接口文件&#xff0c;以qlm_user.…

【红外与可见光融合:条件学习:实例归一化(IN)】

Infrared and visible image fusion based on a two-stage class conditioned auto-encoder network &#xff08;基于两级类条件自编码器网络的红外与可见光图像融合&#xff09; 现有的基于自动编码器的红外和可见光图像融合方法通常利用共享编码器从不同模态中提取特征&am…

arduino安装索尼spresense开发库

arduino安装索尼spresense开发库 一.库安装二.库文件下载1.直接下载2.git下载1.git加速下载2.git下载加速3.将文件导入arduino 一.库安装 打开arduino点击文件->首选项 将以下链接添加进附加开发板管理器网址 https://github.com/sonydevworld/spresense-arduino-compatib…

什么是数据采集与监视控制系统(SCADA)?

SCADA数据采集是一种用于监控和控制工业过程的系统。它可以实时从现场设备获得数据并将其传输到中央计算机&#xff0c;以便进行监控和控制。SCADA数据采集系统通常使用传感器、仪表和控制器收集各种类型的数据&#xff0c;例如温度、压力、流量等&#xff0c;然后将这些数据汇…

【李沐】动手学习ai思路softmax回归实现

来源&#xff1a;https://www.cnblogs.com/blzm742624643/p/15079086.html 一、从零开始实现 1.1 首先引入Fashion-MNIST数据集 1 import torch 2 from IPython import display 3 from d2l import torch as d2l 4 5 batch_size 256 6 train_iter, test_iter d2l.load_data…

tcp流式服务和粘包问题

目录 1.概念 2.流式服务 3.粘包问题 1.概念 套接字是一个全双工的 使用TCP协议通信的双方必须先建立连接,然后才能开始数据的读写,双方都必须为该连接分配必要的内核资源,以管理连接的状态和连接上数据的传输. TCP连接是全双工的,即双方的数据读写可以通过一个连接进行,完成…