生产者和消费者模型 | 阻塞队列 | 信号量 | 环形队列

文章目录

        • 1.生产者和消费者模型
        • 2.生产者和消费者模型优点
        • 3.阻塞队列
        • 4.POSIX信号量
        • 5.基于环形队列的生产消费模型

本文完整的代码放在了这: Gitee链接

1.生产者和消费者模型

生产者和消费者模型,概括起来其实是一个321原则:3是:三种关系,2是两种角色,1是1个交易场所。

三种关系有:生产者和生产者、消费者和消费者、生产者和消费者。

  1. 其中生产者vs生产者之间是:互斥关系。就好比如一家超市,两种品牌的供应商之间是竞争的关系,假设同一时间只有一家供应商能向超市供货,超市就是临界资源。所以用互斥保障安全。
  2. 消费者vs消费者也是互斥关系。如果是在沙漠上,你和你的舍友都非常渴,如果只有一瓶水,那么不得抢起来啊!所以消费者和消费者之间也要保证互斥关系。
  3. 而生产者和消费者则是:互斥和同步的关系。为什么要是互斥呢? 假设超市有一个冰柜,生产者需要往冰柜上放上雪糕,而正好消费者要拿生产者要放到冰柜的拿个雪糕。那么消费者有没有那个雪糕呢?这取决于雪糕还是没放,这是不确定的,在生产者消费者模型等中,要是确定性的。雪糕要么放到冰柜,要么不放到冰柜!不能是将放未放!所以互斥是保障其安全性
    为什么要同步呢? 供应商刚往超市供完货,然后一直打电话问超市要不要供货,电话信道一直被供应商占有,消费者想打电话没东西都没有机会。所以要保障生产者和消费者之间有一定的顺序性,供应商刚刚把货供满就等等,等到消费得差不多了,再来供货。

角色是生成者和消费者。

一个交易场所,上面举例中,我们把超市到当成了交易的场所。在计算机中,这个交易场所就是一块特定的内存空间!

2.生产者和消费者模型优点
  • 多线程站在调度的角度是提高CPU的并发度,而站在编码角度,天然的解耦!生产者消费者模型起到解耦的作用
  • 支持忙闲不均,也就是说,可以支持生产者和消费的处理能力的不同
  • 支持并发,生产者和消费者模型是高效的,为什么呢?首先生产者生产数据可以从用户中来或者网络中来,生产数据是要花费时间的;同样的道理消费者消费数据也是需要花费时间的,在生产者生产数据,消费者消费数据,这同一时刻,它们有很大的概率是并发执行的!它的高效并不能只关注放数据和取数据的角度上!
3.阻塞队列

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

在这里插入图片描述

参考代码

#pragma once    
#include <iostream>
#include <queue>
#include <pthread.h>
#include <unistd.h>

template <class Type>
class bolck_queue
{
public:
    bolck_queue(int max_capacity = 5)
    {
        _max_capacity = max_capacity;
        pthread_mutex_init(&_lock,nullptr);
        pthread_cond_init(&_producer_cond,nullptr);
        pthread_cond_init(&_consumer_cond,nullptr);
        _hight_water = (2/3) * _max_capacity;
        _low_water = (1/3) * _max_capacity;
    }
    ~bolck_queue()
    {
        pthread_mutex_destroy(&_lock);
        pthread_cond_destroy(&_producer_cond);
        pthread_cond_destroy(&_consumer_cond);
    }
public:
    // 生产者,生产操作
    void push(const Type& input)
    {
        pthread_mutex_lock(&_lock);
        /*
        这里(伪唤醒)
        if(_queue.size() == _max_capacity){
            pthread_cond_wait(&_producer_cond,&_lock);
        }
        */
       while (_queue.size() == _max_capacity){
            pthread_cond_wait(&_producer_cond,&_lock);
       }
        // 符合条件,可以生产
        _queue.push(input);
        // 这里生成完毕可以告诉消费者可以消费了
        if(_queue.size() > _hight_water){
            pthread_cond_signal(&_consumer_cond);
        }
        pthread_mutex_unlock(&_lock);
    }
    // 消费者,消费操作
    Type pop()
    {
        pthread_mutex_lock(&_lock);
        // 同理while防止伪唤醒
        while(_queue.size() == 0){
            pthread_cond_wait(&_consumer_cond,&_lock);
        }
        // 符合条件,可以消费
        Type output = _queue.front();
        _queue.pop();
        // 这里消费了,可以告诉生产者来生产了!这里可以定制策略
        if(_queue.size() < _low_water){
            pthread_cond_signal(&_producer_cond);
        }
        pthread_mutex_unlock(&_lock);
        return output;
    }
private:    
    std::queue<Type> _queue;        // 交易场所,缓存(这里把_queue当成一个整体!)
    int _max_capacity;              // 极值,阻塞队列的最大值
    pthread_mutex_t _lock;          // 一把锁,为什么? 保证三种关系的互斥关系!
    pthread_cond_t _producer_cond;  // 生产者条件变量
    pthread_cond_t _consumer_cond;  // 消费者条件变量

    int _hight_water;               // 高水位
    int _low_water;                 // 低水位
};
4.POSIX信号量

POSIX信号量和SystemV信号量作用相同,都是用于同步操作,达到无冲突的访问共享资源目的。 但POSIX可以用于线程间同步。

信号量的本质是计数器! 用来描述资源,当申请信号量时就已经间接的判断临界资源是否就绪,如果申请成功就一定有对应的资源!就好比如,去电影院买票,只有票(信号量)买上了,就一定有对应的位置供你观影使用!同样的线程,成功申请了信号量,那里临界资源一定有线程所能访问的资源。

初始化信号量

int sem_init(sem_t *sem, int pshared, unsigned int value);
参数:
	pshared:0表示线程间共享,非零表示进程间共享
	value:信号量初始值

销毁信号量

int sem_destroy(sem_t *sem);

等待信号量

功能:等待信号量,会将信号量的值减1
int sem_wait(sem_t *sem); // P()操作

发布信号量

功能:发布信号量,表示资源使用完毕,可以归还资源了。将信号量值加1int sem_post(sem_t *sem);// V()操作
5.基于环形队列的生产消费模型

在这里插入图片描述

假设两个人,一个人(生产者)往桌子上摆蛋糕,一个人(消费者)往桌子上取蛋糕。

  1. 当为空或者满时,这个下标对应的位置只有一个人能访问,为空只能时生产者访问,放蛋糕,为满只能是消费者能访问,取蛋糕,其他情况两个人都能同时访问
  2. 生产者不能太快,不能给消费者套一个圈
  3. 消费者者不能太快,不能超过生产者

这里假定两个人,是单生产和单消费!也可以是放蛋糕的人有多个,取蛋糕的人也有多个,对应是多生产多消费!

上面可以通过信号量,来处理生产者和消费者的关系! 生产者关注的是空间(space = N),而消费者关注的是数据(data = 0)

在这里插入图片描述

要通过两个锁,保证生产者VS生产者;消费者VS消费者的互斥关系

参考代码

#pragma once
#include <iostream>
#include <vector>
#include <semaphore.h>
#include <pthread.h>

template <class T>
class ring_queue
{
public:
    ring_queue(int capacity = 5)
        :_ring_queue(capacity)
        ,_capacity(capacity)
        ,_consumer_step(0),_producter_step(0)
    {
        sem_init(&_c_data_sem,0,0);
        sem_init(&_p_space_sem,0,capacity);

        pthread_mutex_init(&_c_lock,nullptr);
        pthread_mutex_init(&_p_lock,nullptr);
    }

    ~ring_queue()
    {
        sem_destroy(&_c_data_sem);
        sem_destroy(&_p_space_sem);
        
        pthread_mutex_destroy(&_c_lock);
        pthread_mutex_destroy(&_p_lock);
    }
private:
    void P(sem_t& sem)
    {
        sem_wait(&sem);
    }
    void V(sem_t& sem)
    {
        sem_post(&sem);
    }
    void lock(pthread_mutex_t& lock)
    {
        pthread_mutex_lock(&lock);
    }
    void unlock(pthread_mutex_t& lock)
    {
        pthread_mutex_unlock(&lock);
    }

public:
    void push(const T& input)
    {
        P(_p_space_sem);
        lock(_p_lock);
        _ring_queue[_producter_step] = input;
        _producter_step++;
        _producter_step %= _capacity;
        unlock(_p_lock);
        V(_c_data_sem);
    }

    T pop()
    {
        /*
            lock(_c_lock);
            P(_c_data_sem);
            为什么不是这样了,加锁放在P操作之前?
            理由1: P是原子的不需要保护,加锁之间的代码要尽可能少
            理由2:要让信号量尽可能去,先获取,当锁一旦释放,里面就能竞争锁,而不是持有锁去
            竞争!就好如:电影院先买票(信号量),然后电影开映直接持有票,进场看电影       
        */
        P(_c_data_sem);
        lock(_c_lock);
        T output = _ring_queue[_consumer_step];
        _consumer_step++;
        _consumer_step %= _capacity;
        unlock(_c_lock);
        V(_p_space_sem);
        return output;
    }
private:
    std::vector<T> _ring_queue;     // 缓冲区,将其看成多份临界资源
    int _capacity;                  // 缓冲区的大小
    int _consumer_step;             // 消费者下标
    int _producter_step;            // 生产者下标
    sem_t _c_data_sem;              // 消费者关注的资源的信号量
    sem_t _p_space_sem;             // 生产者关注的空间的信号量

    pthread_mutex_t _c_lock;        // 解决消费者VS消费者之间的:互斥问题
    pthread_mutex_t _p_lock;        // 解决生产者VS生成者之间的:互斥问题
};

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

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

相关文章

如何恢复未保存或删除/丢失的Word文件?

关闭 Word 应用程序而不保存文档&#xff1f;误删Word文档&#xff1f;许多用户会在不同的情况下丢失Word文档。如果不幸遇到此类问题&#xff0c;如何恢复已删除或未保存的 Word 文档&#xff1f;有一些方法可以恢复未保存/删除的文档。此外&#xff0c;您还可以使用Word文件恢…

前端 接口返回来的照片太大 加载慢如何解决

现象 解决 1. 添加图片懒加载 背景图懒加载 对背景图懒加载做的解释 和图片懒加载不同&#xff0c;背景图懒加载需要使用 v-lazy:background-image&#xff0c;值设置为背景图片的地址&#xff0c;需要注意的是必须声明容器高度。 <div v-for"img in imageList&quo…

【论文笔记】PointMamba: A Simple State Space Model for Point Cloud Analysis

原文链接&#xff1a;https://arxiv.org/abs/2402.10739 1. 引言 基于Transformer的点云分析方法有二次时空复杂度&#xff0c;一些方法通过限制感受野降低计算。这引出了一个问题&#xff1a;如何设计方法实现线性复杂度并有全局感受野。 状态空间模型&#xff08;SSM&…

微服务-2 Eureka

Eureka 启动页面&#xff1a; 同理再注册完order-service后&#xff0c;刷新启动页面&#xff1a; userservice 启动多台服务&#xff1a; [ 代码 ]&#xff1a;orderService.java&#xff08;用 RestTemplate 调其他服务&#xff0c;用 userservice 代替 localhost:8081&…

二叉树——存储结构

二叉树的存储结构 二叉树一般可以使用两种结构存储&#xff0c;一种是顺序结构&#xff0c;另一种是链式结构。 一、顺序存储 二叉树的顺序存储是指用一组连续的存储单元依次自上而下、自左至右存储完全二叉树上的结点元素&#xff0c;即将完全二叉树上编号为i的结点元素存储…

LeetCode 142.环形链表II(数学公式推导)

给定一个链表的头节点 head &#xff0c;返回链表开始入环的第一个节点。 如果链表无环&#xff0c;则返回 null。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统内部使用整…

python图书馆图书借阅系统含网上商城管理系统7d538

&#xff0c;python语言&#xff0c;django框架进行开发&#xff0c;后台使用MySQL数据库进行信息管理&#xff0c;设计开发的图书管理系统。通过调研和分析&#xff0c;系统拥有管理员和用户两个角色&#xff0c;主要具备注册登录、个人信息修改、用户、图书分类、图书信息、借…

多模块项目使用springboot框架进行业务处理

项目目录 1、在Result定义返回结果 package com.edu.result;import lombok.Data;import java.io.Serializable;/*** 后端统一返回结果* param <T>*/ Data public class Result<T> implements Serializable {private Integer code; //编码&#xff1a;1成功&#xf…

【U8+】用友固定资产卡片拆分提示:未设置对象变量或With block变量。

【问题描述】 用友U8软件中&#xff0c; 操作固定资产模块&#xff0c;针对所有资产进行卡片拆分的时候&#xff0c; 提示&#xff1a;未设置对象变量或With block变量。 确定后仍然能打开卡片拆分界面&#xff0c;但是界面显示异常看不到拆分明细信息&#xff0c;并且保存后拆…

LeetCode-Java:303、304区域检索(前缀和)

文章目录 题目303、区域和检索&#xff08;数组不可变&#xff09;304、二维区域和检索&#xff08;矩阵不可变&#xff09; 解①303&#xff0c;一维前缀和②304&#xff0c;二维前缀和 算法前缀和一维前缀和二维前缀和 题目 303、区域和检索&#xff08;数组不可变&#xff…

一些 VLP 下游任务的相关探索

一、Image-Text Retrieval (ITR , 图像文本检索) 任务目的&#xff1a; 检索与给定文本最匹配的图像&#xff0c;或者给定图像最匹配的文本。 跨模态图像-文本检索&#xff08;ITR&#xff09;是根据用户给定的一种模态中的表达&#xff0c;从另一模态中检索出相关样本&#x…

工厂方法模式:解锁灵活的对象创建策略

在软件设计中&#xff0c;工厂方法模式是一种非常实用的创建型设计模式&#xff0c;它不仅提升了系统的灵活性&#xff0c;还简化了对象的创建过程。本文将详细探讨工厂方法模式的核心概念、实现方式、应用场景以及与其他设计模式的对比&#xff0c;旨在提供一份全面且实用的指…

flutter中鼠标检测事件的应用---主要在于网页端使用

flutter中鼠标检测事件的应用—主要在于网页端使用 鼠标放上去 主要代码 import package:flutter/material.dart;class CustomStack extends StatefulWidget {override_CustomStack createState() > _CustomStack(); }class _CustomStack extends State<CustomStack>…

MySQL——创建和插入

一、插入数据 INSERT 使用建议; 在任何情况下建议列出列名&#xff0c;在 VALUES 中插入值时&#xff0c;注意值和列的意义对应关系 values 指定的值顺序非常重要&#xff0c;决定了值是否被保存到正确的列中 在指定了列名的情况下&#xff0c;你可以仅对需要插入的列给到…

C++——StackQueue

目录 一Stack 1介绍 2接口 3模拟实现 4栈的oj题 二Queue 1介绍 2接口 3模拟实现 三容器适配器 1再谈栈和队列 四优先级队列 1接口 ​编辑 2仿函数 五dequeue的简单介绍 一Stack 1介绍 先来看看库中对栈的介绍&#xff1a; 1. stack是一种容器适配器&#x…

自养号测评技术:如何快速提高亚马逊、速卖通、沃尔玛新号的权重?

亚马逊平台高度关注买家账号权重&#xff0c;对于希望快速提升listing曝光与销量的卖家而言&#xff0c;拥有高权重买家账号进行下单至关重要。前提是&#xff0c;确保您的listing已经过精细优化。当前&#xff0c;市场上存在众多自养号服务商&#xff0c;然而由于多种原因&…

外汇兑换问题的最优子结构分析

外汇兑换问题的最优子结构分析 一、 当所有交易佣金为零时1.1 伪代码示例&#xff1a;1.2 C代码示例 二、 佣金不为零时的最优子结构性质三、 结论 在考虑外汇兑换问题时&#xff0c;我们面临的是如何通过一系列兑换操作&#xff0c;以最小的成本将一种货币转换为另一种货币。这…

网络变压器(网络隔离变压器)是如何影响网通设备的传输速率的呢?

Hqst华轩盛(石门盈盛)电子导读&#xff1a;今天介绍网络变压器&#xff08;网络隔离变压器/网络滤波器&#xff09;是如何影响网通设备的传输速率的 一、网络变压器&#xff08;网络隔离变压器/网络滤波器&#xff09;的工作原理 网络变压器&#xff08;网络隔离变压器/网络滤…

Docker 学习笔记(六):挑战容器数据卷技术一文通,实战多个 MySQL 数据同步,能懂会用,初学必备

一、前言 记录时间 [2024-4-11] 系列文章简摘&#xff1a; Docker学习笔记&#xff08;二&#xff09;&#xff1a;在Linux中部署Docker&#xff08;Centos7下安装docker、环境配置&#xff0c;以及镜像简单使用&#xff09; Docker 学习笔记&#xff08;三&#xff09;&#x…

arm-linux-gnueabihf-gcc默认目录

默认编译的头文件目录&#xff1a; /usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/arm-linux-gnueabihf/lib 默认编译的库文件目录&#xff1a; /usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/arm-linux-gnueabihf/include/ …