Linux之生产消费者模型

news2025/2/25 23:21:32

(。・∀・)ノ゙嗨!你好这里是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_;
};

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

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

相关文章

unity学习(53)——选择角色界面--分配服务器返回的信息

好久没写客户端了&#xff0c;一上手还不太适应 1.经过测试&#xff0c;成功登陆后&#xff0c;客户端请求list_request&#xff0c;成功返回&#xff0c;如下图&#xff1a; 可见此时model第三个位置的参数是1.也成功返回了所有已注册角色的信息。 2.之前已知创建的角色信息…

计算机服务器中了locked勒索病毒怎么解密,locked勒索病毒解密流程

科技的发展带动了企业生产&#xff0c;越来越多的企业开始利用计算机服务器办公&#xff0c;为企业的生产运营提供了极大便利&#xff0c;但随之而来的网络安全威胁也引起了众多企业的关注。近日&#xff0c;云天数据恢复中心接到许多企业的求助&#xff0c;企业的计算机服务器…

(每日持续更新)信息系统项目管理(第四版)(高级项目管理)考试重点整理 第13章 项目资源管理(七)

项目建议与立项申请、初步可行性研究、详细可行性研究、评估与决策是项目投资前使其的四个阶段。在实际工作中&#xff0c;初步可行性研究和详细可行性研究可以依据项目的规模和繁简程度合二为一&#xff0c;但详细可行性研究是不可缺少的。升级改造项目制作初步和详细研究&…

【01背包与完全背包】Aswing

01 https://www.acwing.com/problem/content/description/2/ #include<bits/stdc.h>using namespace std;const int MAXN 1005; int v[MAXN]; // 体积 int w[MAXN]; // 价值 int f[MAXN][MAXN]; // f[i][j], j体积下前i个物品的最大价值 int main() {int n,…

大模型产业落地,安全运营能否迎来“自动驾驶”时刻?

科技云报道原创。 通过一段文字描述&#xff0c;就能生成60秒堪比大片的视频&#xff0c;来自大模型Sora的出色表现&#xff0c;让全球都为之震撼。 无论是ChatGPT还是Sora&#xff0c;都只是大模型走出实验室的第一步&#xff0c;大模型如何在产业中落地&#xff0c;为具体的…

PyTorch搭建LeNet训练集详细实现

一、下载训练集 导包 import torch import torchvision import torch.nn as nn from model import LeNet import torch.optim as optim import torchvision.transforms as transforms import matplotlib.pyplot as plt import numpy as npToTensor()函数&#xff1a; 把图像…

多维时序 | Matlab实现BiGRU-Mutilhead-Attention双向门控循环单元融合多头注意力机制多变量时序预测

多维时序 | Matlab实现BiGRU-Mutilhead-Attention双向门控循环单元融合多头注意力机制多变量时序预测 目录 多维时序 | Matlab实现BiGRU-Mutilhead-Attention双向门控循环单元融合多头注意力机制多变量时序预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.多维时序 …

C++笔记之给枚举类型的变量赋值

C++笔记之给枚举类型的变量赋值 —— 杭州 2024-03-10 code review! 在C++中,你可以在结构体内部定义一个枚举类型,并在创建结构体变量时给枚举类型的变量赋值。下面是一个简单的例子展示了如何做到这一点: 代码 #include <iostream>// 定义结构体 struct MyStru…

虚函数与纯虚函数有什么区别?

总的来说有两点区别&#xff1a; 1.虚函数的作用主要是矫正指针&#xff08;口语化的说法&#xff09; 2.虚函数不一定要重新定义&#xff0c;纯虚函数一定要定义&#xff08;口语化的说法&#xff09; 1&#xff09;. 虚函数的作用主要是矫正指针&#xff0c;使得基类的指针…

ArrayDeque集合源码分析

ArrayDeque集合源码分析 文章目录 ArrayDeque集合源码分析一、字段分析二、构造函数分析方法、方法分析四、总结 实现了 Deque&#xff0c;说面该数据结构一定是个双端队列&#xff0c;我们知道 LinkedList 也是双端队列&#xff0c;并且是用双向链表 存储结构的。而 ArrayDequ…

深入探讨AI团队的角色分工

目录 前言1. 软件工程师&#xff1a;构建系统基石的关键执行者2. 机器学习工程师&#xff1a;数据与模型的塑造专家3. 机器学习研究员&#xff1a;引领算法创新的智囊4. 机器学习应用科学家&#xff1a;理论与实践的巧妙连接5. 数据分析师&#xff1a;洞察数据&#xff0c;智慧…

如何在Linux上为PyCharm创建和配置Desktop Entry

在Linux操作系统中&#xff0c;.desktop 文件是一种桌面条目文件&#xff0c;用于在图形用户界面中添加程序快捷方式。本文将指导您如何为PyCharm IDE创建和配置一个 .desktop 文件&#xff0c;从而能够通过应用程序菜单或桌面图标快速启动PyCharm。 步骤 1: 确定PyCharm安装路…

力扣中档题:删除排序链表中的 重复元素

此题可以选择暴力解决&#xff0c;首先将链表中的元素放到数组中&#xff0c;然后将数组中的重复元素放到另一个数组中&#xff0c;最后再判断并将目标值放到第三个数组中排序再改链表&#xff0c;注意链表nextNULL的操作 struct ListNode* deleteDuplicates(struct ListNode*…

【Nestjs实操】环境变量和全局配置

一、环境变量 1、使用dotenv 安装pnpm add dotenv。 根目录下创建.env文件&#xff0c;内容如下&#xff1a; NODE_ENVdevelopment使用 import {config} from "dotenv"; const path require(path); config({path:path.join(__dirname,../.env)}); console.log(…

简介:基于 OpenTiny 组件库的 rendereless 无渲染组件架构

在 HAE 自研阶段&#xff0c;我们实现的数据双向绑定、面向对象的 JS 库、配置式开发的注册表等特性&#xff0c;随着前端技术的高速发展现在已经失去存在的意义&#xff0c;但是在 AUI 阶段探索的新思路新架构&#xff0c;经过大量的业务落地验证&#xff0c;再次推动前端领域…

万用表数据导出变化曲线图——pycharm实现视频数据导出变化曲线图

万用表数据导出变化曲线图——pycharm实现视频数据导出变化曲线图 一、效果展示二、环境配置三、代码构思四、代码展示五、代码、python环境包链接 一、效果展示 图1.1 效果展示 &#xff08;左图&#xff1a;万用表视频截图&#xff1b;右图&#xff1a;表中数据变化曲线图&am…

宽度优先搜索算法(BFS)

宽度优先搜索算法&#xff08;BFS&#xff09;是什么&#xff1f; 宽度优先搜索算法&#xff08;BFS&#xff09;&#xff08;也称为广度优先搜索&#xff09;主要运用于树、图和矩阵&#xff08;这三种可以都归类在图中&#xff09;&#xff0c;用于在图中从起始顶点开始逐层…

字节跳动的 SDXL-LIGHTNING : 体验飞一般的文生图

TikTok 的母公司字节跳动推出了最新的文本到图像生成人工智能模型&#xff0c;名为SDXL-Lightning。顾名思义&#xff0c;这个新模型只需很轻量的推理步骤&#xff08;1&#xff0c;4 或 8 步&#xff09;即可实现极其快速且高质量的文本到图像生成功能。与原始 SDXL 模型相比&…

嵌入式 Linux 学习

在学习嵌入式 Linux 之前&#xff0c;我们先来了解一下嵌入式 Linux 有哪些东西。 1. 嵌入式 Linux 的组成 嵌入式 Linux 系统&#xff0c;就相当于一套完整的 PC 软件系统。 无论你是 Linux 电脑还是 windows 电脑&#xff0c;它们在软件方面的组成都是类似的。 我们一开电…

.NET高级面试指南专题十六【 装饰器模式介绍,包装对象来包裹原始对象】

装饰器模式&#xff08;Decorator Pattern&#xff09;是一种结构型设计模式&#xff0c;用于动态地给对象添加额外的职责&#xff0c;而不改变其原始类的结构。它允许向对象添加行为&#xff0c;而无需生成子类。 实现原理&#xff1a; 装饰器模式通过创建一个包装对象来包裹原…