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_;
};

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深入理解 Webpack 热更新原理:提升开发效率的关键

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

从零开始:神经网络(1)——神经元和梯度下降

声明&#xff1a;本文章是根据网上资料&#xff0c;加上自己整理和理解而成&#xff0c;仅为记录自己学习的点点滴滴。可能有错误&#xff0c;欢迎大家指正。 一. 神经网络 1. 神经网络的发展 先了解一下神经网络发展的历程。从单层神经网络&#xff08;感知器&#xff09;开…

selenium元素定位问题

具体网页信息如下&#xff1a; 定位的时候driver.find_element(By.CLASS_NAME, 方法搞不定。 定位方法&#xff1a; 方法一&#xff1a;通过文本定位 driver.find_element(By.XPATH, "//*[text()高分一号]").click() time.sleep(3) 如果是部分文字 #部分文字py…

安卓提示风险解决源码搭建教程

一&#xff0e;环境 1.安装Nginx 2.安装Tomcat8.5 3. 安装Mysql5.7 二&#xff0e;修改app已生成的文件下载地址 1.打开编辑config.properties 2.填写你的ip&#xff0c;端口不用修改 三&#xff0e;启动教程 启动命令&#xff1a;sh.start.sh 源码下载链接:https://p…

贪心算法(蓝桥杯 C++ 题目 代表 注解)

介绍&#xff1a; 贪心算法&#xff08;Greedy Algorithm&#xff09;是一种在每一步选择中都采取当前状态下最好或最优&#xff08;即最有利&#xff09;的选择&#xff0c;从而希望最终能够得到全局最好或最优的结果的算法。它通常用来解决一些最优化问题&#xff0c;如最小生…

JVM基本概念、命令、参数、GC日志总结

原文: 赵侠客 一、前言 NPE&#xff08;NullPointerException&#xff09;和OOM&#xff08;OutofMemoryError&#xff09;在JAVA程序员中扮演着重要的角色&#xff0c;它也是很多人始终摆脱不掉的梦魇&#xff0c;与NPE不同的是OOM一旦在生产环境中出现就意味着只靠代码已经无…

FPGA的配置状态字寄存器Status Register

目录 简介 状态字定义 Unknown Device/Many Unknow Devices 解决办法 一般原因 简介 Xilinx的FPGA有多种配置接口&#xff0c;如SPI&#xff0c;BPI&#xff0c;SeletMAP&#xff0c;Serial&#xff0c;JTAG等&#xff1b;如果从时钟发送者的角度分&#xff0c;还可以…

读书笔记之《理解和改变世界》:从信息知识智能的本质看AI

《理解和改变世界: 从信息到知识与智能》作者:是(法) 约瑟夫希发基思&#xff0c; 原作名: Understanding and Changing the World: From Information to Knowledge and Intelligence&#xff0c;2023年出版。 约瑟夫希发基思&#xff08;Joseph Sifakis&#xff09;&#xff…

Java高频面试之并发篇

有需要互关的小伙伴,关注一下,有关必回关,争取今年认证早日拿到博客专家 并行和并发有什么区别&#xff1f; 并行是同时执行多个任务&#xff0c;而并发是多个任务在一段时间内交替执行。并行&#xff08;Parallel&#xff09;是指同时执行多个任务或操作&#xff0c;通过同时…

Java开发:对象间复制属性,方法归纳

在Java开发中&#xff0c;对象间复制属性是一项常见的任务&#xff0c;特别是在处理层&#xff08;如控制器层&#xff09;与服务层或数据传输对象&#xff08;DTOs&#xff09;之间的数据转换时。有多种方法可以实现User对象到UserDTO对象的属性复制&#xff0c;下面列举了几种…

浅析开源内存数据库Fastdb

介绍&#xff1a; Fastdb是免费开源内存数据库&#xff0c;其优秀的性能&#xff0c;和简洁的C代码&#xff0c;让我学习使用过程中收益颇多&#xff0c;但是国内中文相关研究的文章相当稀少&#xff0c;外文我查询相当不便。有兴趣的朋友可以通过以下网站访问&#xff1a;Mai…

深入浅出计算机网络 day.1 概论③ 电路交换、分组交换和报文交换

人无法同时拥有青春和对青春的感受 —— 04.3.9 内容概述 01.电路交换、分组交换和报文交换 02.三种交换方式的对比 一、电路交换、分组交换和报文交换 1.电路交换 计算机之间的数据传送是突发式的&#xff0c;当使用电路交换来传送计算机数据时&#xff0c;其线路的传输效率一…