【线程】基于环形队列的生产者消费者模型

1 环形队列

环形队列采用数组来模拟,用取模运算来模拟环状特性。
在这里插入图片描述
1.如何判断环形队列为空或者为满?

  • 当环形队列为时,头和尾都指向同一个位置。
  • 当环形队列为时,头和尾也都指向同一个位置。

因此, 可以通过加计数器或者标记位来判满或者空,也可以预留一个空的位置,作为满的状态

1.1 生产者消费者中的环形队列

2.生产者和消费者什么时候会访问同一个位置?

1.环形队列为空的时候,生产者和消费者会访问同一个位置。
在这里插入图片描述
环形队列中,如果队列为空,那么队首和队尾的指针是指向同一个位置的。所以,生产者和消费者也会指向同一个位置。

2.环形队列为满的时候,生产者和消费者会访问同一个位置。
在这里插入图片描述
环形队列中,如果队列为满,那么队首和队尾的指针是指向同一个位置的。所以,生产者和消费者也会指向同一个位置。

其他任何时候,生产者和消费者访问的都是不同的区域。
在这里插入图片描述

3.环形队列中的数据需要注意什么?

1.消费者不能超过生产者。
在这里插入图片描述
也就是: 不能没有数据了还继续消费

2.生产者不能将消费者套圈。
在这里插入图片描述
也就是: 不能没有空间了还继续生产

2 设计信号量

生产者最在意的是空闲的空间个数
消费者最在意的是数据的个数

所以生产者每次在访问临界资源之前,需要先申请空间资源的信号量,申请成功就可以进行生产,否则就阻塞等待。

消费者在访问临界资源之前,需要申请数据资源的信号量,申请成功就可以消费数据,否则就阻塞等待。

空间资源信号量的申请由生产者进行,归还(V操作)由消费者进行,表示生产者可以生产数据。

数据资源信号量的申请有消费者进行,归还(V操作)由生产者进行,表示消费者可以进行消费.

4.空间资源信号如何设计?

最开始,生产者没有生产,所以空间资源是队列的大小(满的)

5.数据资源信号如何设计?

最开始,生产者没有生产,所以数据资源为0(空的)

2.1 生产者伪代码

productor_sem = 环形队列大小;P(productor_sem);//申请空间资源信号量
//申请成功,继续向下运行。
//申请失败,阻塞在申请处。.......//从事生产活动——把数据放入队列中V(comsumer_sem);//归还数据资源信号量

2.2 消费者伪代码

comsumer_sem = 0;P(comsumer_sem);//申请数据资源信号量
//申请成功,继续向下运行。
//申请失败,阻塞在申请处。.......//从事消费活动——从队列中消费数据V(proudctor_sem);//归还空间资源信号量

在环形队列中,大部分情况下单生产和单消费是可以并发执行的,只有在满或者空的时候,才会有同步和互斥问题,同步和互斥是通过信号量来实现的。

3 代码

3.1 RingQueue.h

#include <iostream>
#include <vector>
#include <semaphore.h>
#include <pthread.h>//为什么基于阻塞队列的生产者消费者模型只需要一个锁 , 而基于环形队列的生产者消费者模型需要两个锁?
//1.因为阻塞队列是对同一个队列的同一个位置进行操作,队列是共享资源,需要对整个队列加锁
//2.阻塞队列中,生产者和消费者访问的不是同一个下标,所以两者是互不干扰的,只需要用条件变量来唤醒阻塞
//但是生产者对生产者而言,是需要加锁的。消费者对消费者而言,是需要加锁的。template <class T>
class RingQueue
{static const int defaultnum = 5;
public://p操作,申请信号量,信号量--void p(sem_t& sem){sem_wait(&sem);}//v操作,释放信号量,信号量++void v(sem_t& sem){sem_post(&sem);}void Lock(pthread_mutex_t& mutex){pthread_mutex_lock(&mutex);}void UnLock(pthread_mutex_t& mutex){pthread_mutex_unlock(&mutex);}public:RingQueue(int cap = defaultnum):_ringqueue(cap),_cap(cap),_c_step(0),_p_step(0){//初始化信号量,给消费者初始的信号量为0,给生产者初始的信号量为cap(满)//因为最开始的时候没有商品,无法消费,只能生产sem_init(&_c_data_sem, 0, 0);sem_init(&_p_space_sem, 0, cap);pthread_mutex_init(&_c_mutex, nullptr);pthread_mutex_init(&_p_mutex, nullptr);}//生产商品void push(const T &in){   //1.申请信号量p(_p_space_sem);//2.消费者加锁Lock(_p_mutex);//3.进行生产_ringqueue[_p_step] = in;_p_step++;_p_step %= _cap;  //保证下标一直都在环形队列里面//4.解锁UnLock(_p_mutex);//5.释放信号量v(_c_data_sem);}void pop(T* out){//1.申请信号量p(_c_data_sem);//2.申请锁Lock(_c_mutex);//3.消费数据*out = _ringqueue[_c_step];++_c_step;_c_step %= _cap;//4.解锁UnLock(_c_mutex);//5.生产者信号量++v(_p_space_sem);  //消费完数据之后,生产者的信号量++}
private:std::vector<T> _ringqueue;int _cap;     //capacityint _c_step;  //消费者在环形队列中的下标int _p_step;  //生产者在环形队列中的下标sem_t _c_data_sem;  //消费者关注的数据资源sem_t _p_space_sem; //生产者关注的消费资源pthread_mutex_t _c_mutex;   //消费者锁pthread_mutex_t _p_mutex;   //生产者锁
};

3.1.1 生产商品

//生产商品void push(const T &in){   //1.申请信号量p(_p_space_sem);//2.消费者加锁Lock(_p_mutex);//3.进行生产_ringqueue[_p_step] = in;_p_step++;_p_step %= _cap;  //保证下标一直都在环形队列里面//4.解锁UnLock(_p_mutex);//5.释放信号量v(_c_data_sem);}

6.为什么要按照 申请信号量 → 加锁 → 解锁 → 释放信号量 的顺序来做?

如果先加锁再申请信号量,可能会出现以下这种情况:
加锁之后,发现没有空闲空间了,于是阻塞。而此时该线程还持有锁,就会导致死锁。.
如果先释放信号量再解锁,可能会出现以下这种情况:
释放信号量之后,消费者得知有可以消费的资源,于是开始消费。但是此时并没有解锁,因此生产者可能并没有完全完成生产,但是消费者已经开始消费。就会导致生产消费数据不一致的问题。.

3.1.2 消费商品

void pop(T* out){//1.申请信号量p(_c_data_sem);//2.申请锁Lock(_c_mutex);//3.消费数据*out = _ringqueue[_c_step];++_c_step;_c_step %= _cap;//4.解锁UnLock(_c_mutex);//5.生产者信号量++v(_p_space_sem);  //消费完数据之后,生产者的信号量++}

在这里插入图片描述

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

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

相关文章

Vue指令v-html

目录 一、Vue中的v-html指令是什么&#xff1f;二、v-html指令与v-text指令的区别&#xff1f; 一、Vue中的v-html指令是什么&#xff1f; v-html指令的作用是&#xff1a;设置元素的innerHTML&#xff0c;内容中有html结构会被解析为标签。 二、v-html指令与v-text指令的区别…

OPENGLPG第九版学习 - 着色器基础

文章目录 2.1 着色器与OpenGL2.2 0penGL的可编程管线2.3 OpenGL着色语言GLSL概述2.3.1 使用GLSL构建着色器变量的声明变量的作用域变量的初始化构造函数 、 类型转换聚合类型访问向量和矩阵中的元素结构体数组多维数组 2.3.2 存储限制符const 存储限制符in 存储限制符out 存储限…

路径规划之启发式算法之二十九:鸽群算法(Pigeon-inspired Optimization, PIO)

鸽群算法(Pigeon-inspired Optimization, PIO)是一种基于自然界中鸽子群体行为的智能优化算法,由Duan等人于2014年提出。该算法模拟了鸽子在飞行过程中利用地标、太阳和磁场等导航机制的行为,具有简单、高效和易于实现的特点,适用于解决连续优化问题。 更多的仿生群体算法…

Docker Compose的使用

文章首发于我的博客&#xff1a;https://blog.liuzijian.com/post/docker-compose.html 目录 Docker Compose是什么Docker Compose安装Docker Compose文件Docker Compose常用命令案例&#xff1a;部署WordPress博客系统 Docker Compose是什么 Docker Compose是Docker官方的开源…

AP单类平均准确率

P_true N_true P_pred TP Fp N_pred FN TNP NTP&#xff08;真正样本&#xff0c;与真实框IoU大于阈值的框&#xff09; FP&#xff08;假正样本&#xff0c;与真实框IoU小于阈值的框&#xff09; TN&#xff08;真负样本&#xff0c;背景&#xff09;…

Leetcode—1427. 字符串的左右移【简单】Plus

2025每日刷题&#xff08;206&#xff09; Leetcode—1427. 字符串的左右移 实现代码 class Solution { public:string stringShift(string s, vector<vector<int>>& shift) {// shift[i] [dir, amount]// dir 0(左) or 1(右)// 左表示正, 右表示负int len…

机器学习10

自定义数据集 使用scikit-learn中svm的包实现svm分类 代码 import numpy as np import matplotlib.pyplot as pltclass1_points np.array([[1.9, 1.2],[1.5, 2.1],[1.9, 0.5],[1.5, 0.9],[0.9, 1.2],[1.1, 1.7],[1.4, 1.1]])class2_points np.array([[3.2, 3.2],[3.7, 2.9],…

股票入门知识

股票入门&#xff08;更适合中国宝宝体制&#xff09; 股市基础知识 本文介绍了股票的基础知识&#xff0c;股票的分类&#xff0c;各板块发行上市条件&#xff0c;股票代码&#xff0c;交易时间&#xff0c;交易规则&#xff0c;炒股术语&#xff0c;影响股价的因素&#xf…

Golang 并发机制-3:通道(channels)机制详解

并发编程是一种创建性能优化且响应迅速的软件的强大方法。Golang&#xff08;也称为 Go&#xff09;通过通道&#xff08;channels&#xff09;这一特性&#xff0c;能够可靠且优雅地实现并发通信。本文将揭示通道的概念&#xff0c;解释其在并发编程中的作用&#xff0c;并提供…

C#,入门教程(11)——枚举(Enum)的基础知识和高级应用

上一篇&#xff1a; C#&#xff0c;入门教程(10)——常量、变量与命名规则的基础知识https://blog.csdn.net/beijinghorn/article/details/123913570 不会枚举&#xff0c;就不会编程&#xff01; 枚举 一个有组织的常量系列 比如&#xff1a;一个星期每一天的名字&#xf…

读书笔记--分布式架构的异步化和缓存技术原理及应用场景

本篇是在上一篇的基础上&#xff0c;主要对分布式应用架构下的异步化机制和缓存技术进行学习&#xff0c;主要记录和思考如下&#xff0c;供大家学习参考。大家知道原来传统的单一WAR应用中&#xff0c;由于所有数据都在同一个数据库中&#xff0c;因此事务问题一般借助数据库事…

【C++】继承(下)

大家好&#xff0c;我是苏貝&#xff0c;本篇博客带大家了解C的继承&#xff08;下&#xff09;&#xff0c;如果你觉得我写的还不错的话&#xff0c;可以给我一个赞&#x1f44d;吗&#xff0c;感谢❤️ 目录 5.继承与友元6.继承与静态成员7.复杂的菱形继承及菱形虚拟继承8.继…

基于LLM的路由在专家混合应用:一种新颖的交易框架,该框架在夏普比率和总回报方面提升了超过25%

“LLM-Based Routing in Mixture of Experts: A Novel Framework for Trading” 论文地址&#xff1a;https://arxiv.org/pdf/2501.09636 摘要 随着深度学习和大语言模型&#xff08;LLMs&#xff09;的不断进步&#xff0c;混合专家&#xff08;MoE&#xff09;机制在股票投资…

Med-R2:基于循证医学的检索推理框架:提升大语言模型医疗问答能力的新方法

Med-R2 : Crafting Trustworthy LLM Physicians through Retrieval and Reasoning of Evidence-Based Medicine Med-R2框架Why - 这个研究要解决什么现实问题What - 核心发现或论点是什么How - 1. 前人研究的局限性How - 2. 你的创新方法/视角How - 3. 关键数据支持How - 4. 可…

【Blazor学习笔记】.NET Blazor学习笔记

我是大标题 我学习Blazor的顺序是基于Blazor University&#xff0c;然后实际内容不完全基于它&#xff0c;因为它的例子还是基于.NET Core 3.1做的&#xff0c;距离现在很遥远了。 截至本文撰写的时间&#xff0c;2025年&#xff0c;最新的.NET是.NET9了都&#xff0c;可能1…

C++ Primer 迭代器

欢迎阅读我的 【CPrimer】专栏 专栏简介&#xff1a;本专栏主要面向C初学者&#xff0c;解释C的一些基本概念和基础语言特性&#xff0c;涉及C标准库的用法&#xff0c;面向对象特性&#xff0c;泛型特性高级用法。通过使用标准库中定义的抽象设施&#xff0c;使你更加适应高级…

2 [GitHub遭遇严重供应链投毒攻击]

近日&#xff0c;有黑客针对 Discord Top.gg 的GitHub 账户发起了供应链攻击&#xff0c;此次攻击导致账户密码、凭证和其他敏感信息被盗&#xff0c;同时也影响到了大量开发人员。 Checkmarx 在一份技术报告中提到&#xff0c;黑客在这次攻击中使用了多种TTP&#xff0c;其中…

【AudioClassificationModelZoo-Pytorch】基于Pytorch的声音事件检测分类系统

源码&#xff1a;https://github.com/Shybert-AI/AudioClassificationModelZoo-Pytorch 模型测试表 模型网络结构batch_sizeFLOPs(G)Params(M)特征提取方式数据集类别数量模型验证集性能EcapaTdnn1280.486.1melUrbanSound8K10accuracy0.974, precision0.972 recall0.967, F1-s…

基于Spring Security 6的OAuth2 系列之七 - 授权服务器--自定义数据库客户端信息

之所以想写这一系列&#xff0c;是因为之前工作过程中使用Spring Security OAuth2搭建了网关和授权服务器&#xff0c;但当时基于spring-boot 2.3.x&#xff0c;其默认的Spring Security是5.3.x。之后新项目升级到了spring-boot 3.3.0&#xff0c;结果一看Spring Security也升级…

Jupyterlab和notebook修改文件的默认存放路径的方法

文章目录 1.缘由2.操作流程2.1找到默认的路径2.2创建配置文件2.3修改配置文件内容2.4注意事项 1.缘由 我自己使用jupyterlab的时候&#xff0c;打开是在这个浏览器上面打开的&#xff0c;但是这个打开的文件路径显示的是C盘上面路径&#xff0c;所以这个就很麻烦&#xff0c;因…