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.pswp.cn/web/67866.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/67866.shtml
英文地址,请注明出处:http://en.pswp.cn/web/67866.shtml

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

相关文章

二分/双指针/单调栈队列专题

1.4924. 矩阵 - AcWing题库 一开始打表找规律以为是右上角向左下角递增,但当n很大的时候就不对了,因此我们得去观察 i * i 100000 * (i - j) j * j i * j 这个式子,我们关心的是这个式子的单调性因此我们可以分别将i和j看作常数来对式子进行求导,可以得到 f(i) 2 * i 10…

Shell $0

个人博客地址&#xff1a;Shell $0 | 一张假钞的真实世界 我们已经知道在Shell中$0表示Shell脚本的文件名&#xff0c;但在有脚本调用的情形中&#xff0c;子脚本中的$0会是什么值呢&#xff1f;我们通过下面的实例来看。 已测试系统列表&#xff1a; Mac OS X EI Capitan 1…

商品列表及商品详情展示

前言 本文将展示一段结合 HTML、CSS 和 JavaScript 的代码&#xff0c;实现了一个简单的商品展示页面及商品详情&#xff0c;涵盖数据获取、渲染、搜索及排序等功能。 效果展示 点击不同的商品会展示对应的商品详情。 代码部分 代码总体实现 <!DOCTYPE html> <htm…

[ VS Code 插件开发 ] 使用 Task ( 任务 ) 代替 createTerminal (终端) 来执行命令

VSCode 官方自己的插件就是这样执行命令的. 使用体验 比 默认的终端 好太多了. 重用终端, Shell 集成 , 按任意键关闭, 任务是否成功, 左侧命令操作 (菜单中功能很多) 等 import * as vscode from vscode; // 执行的命令 let command_str "npm run dev" // 工作目…

大模型综述一镜到底(全文八万字) ——《Large Language Models: A Survey》

论文链接&#xff1a;https://arxiv.org/abs/2402.06196 摘要&#xff1a;自2022年11月ChatGPT发布以来&#xff0c;大语言模型&#xff08;LLMs&#xff09;因其在广泛的自然语言任务上的强大性能而备受关注。正如缩放定律所预测的那样&#xff0c;大语言模型通过在大量文本数…

Python处理数据库:MySQL与SQLite详解

Python处理数据库&#xff1a;MySQL与SQLite详解 在数据处理和存储方面&#xff0c;数据库扮演着至关重要的角色。Python提供了多种与数据库交互的方式&#xff0c;其中pymysql库用于连接和操作MySQL数据库&#xff0c;而SQLite则是一种轻量级的嵌入式数据库&#xff0c;Pytho…

【C++】B2124 判断字符串是否为回文

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 &#x1f4af;前言&#x1f4af;题目描述输入格式&#xff1a;输出格式&#xff1a;样例&#xff1a; &#x1f4af;方法一&#xff1a;我的第一种做法思路代码实现解析 &#x1f4af;方法二&#xff1a;我…

ubuntuCUDA安装

系列文章目录 移动硬盘制作Ubuntu系统盘 前言 根据前篇“移动硬盘制作Ubuntu系统盘”安装系统后&#xff0c;还不能够使用显卡。 如果需要使用显卡&#xff0c;还需要进行相关驱动的安装&#xff08;如使用的为Nvidia显卡&#xff0c;就需要安装相关的Nvidia显卡驱动&#xff…

Selenium 使用指南:从入门到精通

Selenium 使用指南&#xff1a;从入门到精通 Selenium 是一个用于自动化 Web 浏览器操作的强大工具&#xff0c;广泛应用于自动化测试和 Web 数据爬取中。本文将带你从入门到精通地掌握 Selenium&#xff0c;涵盖其基本操作、常用用法以及一个完整的图片爬取示例。 1. 环境配…

Sqoop导入MySQL中含有回车换行符的数据

个人博客地址&#xff1a;Sqoop导入MySQL中含有回车换行符的数据 MySQL中的数据如下图&#xff1a; 检查HDFS上的目标文件内容可以看出&#xff0c;回车换行符位置的数据被截断了&#xff0c;导致数据列错位。 Sqoop提供了配置参数&#xff0c;在导入时丢弃掉数据的分隔符&…

利用matlab寻找矩阵中最大值及其位置

目录 一、问题描述1.1 max函数用法1.2 MATLAB中 : : :的作用1.3 ind2sub函数用法 二、实现方法2.1 方法一&#xff1a;max和find2.2 方法二&#xff1a;max和ind2sub2.3 方法对比 三、参考文献 一、问题描述 matlab中求最大值可使用函数max&#xff0c;对于一维向量&#xff0…

PyTorch数据建模

回归分析 import torch import numpy as np import pandas as pd from torch.utils.data import DataLoader,TensorDataset import time strat = time.perf_counter()

机试题——字符匹配

题目描述 给你一个字符串数组&#xff08;每个字符串均由小写字母组成&#xff09;和一个字符规律&#xff08;由小写字母和 . 和 * 组成&#xff09;&#xff0c;识别数组中哪些字符串可以匹配到字符规律上。 . 匹配任意单个字符。* 匹配零个或多个前面的那一个元素。 所谓…

《 C++ 点滴漫谈: 二十五 》空指针,隐秘而危险的杀手:程序崩溃的真凶就在你眼前!

摘要 本博客全面解析了 C 中指针与空值的相关知识&#xff0c;从基础概念到现代 C 的改进展开&#xff0c;涵盖了空指针的定义、表示方式、使用场景以及常见注意事项。同时&#xff0c;深入探讨了 nullptr 的引入及智能指针在提升代码安全性和简化内存管理方面的优势。通过实际…

git push到远程仓库时无法推送大文件

一、错误 remote: Error: Deny by project hooks setting ‘default’: size of the file ‘scientific_calculator’, is 164 MiB, which has exceeded the limited size (100 MiB) in commit ‘4c91b7e3a04b8034892414d649860bf12416b614’. 二、原因 本地提交过大文件&am…

掌握API和控制点(从Java到JNI接口)_36 JNI开发与NDK 04

4、 *.so的入口函数&#xff1a;JNI_OnLoad() VM (virtual machine)的角色 Java代码在VM上执行。在执行Java代码的过程中&#xff0c;如果Java需要与本地代码(*.so)沟通时&#xff0c; VM就会把*.so視为插件<Tn>而加载到VM里。然后让Java函数呼叫到这插件<Tn>里的…

Windows图形界面(GUI)-QT-C/C++ - QT Tab Widget

公开视频 -> 链接点击跳转公开课程博客首页 -> ​​​链接点击跳转博客主页 目录 一、概述 1.1 什么是 QTabWidget&#xff1f; 1.2 使用场景 二、常见样式 2.1 选项卡式界面 2.2 动态添加和删除选项卡 2.3 自定义选项卡标题和图标 三、属性设置 3.1 添加页面&…

[MRCTF2020]Ez_bypass1(md5绕过)

[MRCTF2020]Ez_bypass1(md5绕过) ​​ 这道题就是要绕过md5强类型比较&#xff0c;但是本身又不相等&#xff1a; md5无法处理数组&#xff0c;如果传入的是数组进行md5加密&#xff0c;会直接放回NULL&#xff0c;两个NuLL相比较会等于true&#xff1b; 所以?id[]1&gg…

90,【6】攻防世界 WEB Web_php_unserialize

进入靶场 进入靶场 <?php // 定义一个名为 Demo 的类 class Demo { // 定义一个私有属性 $file&#xff0c;默认值为 index.phpprivate $file index.php;// 构造函数&#xff0c;当创建类的实例时会自动调用// 接收一个参数 $file&#xff0c;用于初始化对象的 $file 属…

Jenkins安装部署(以及常见报错解决方案),jdk版本控制器sdkman

目录 零、环境介绍 一、Jenkins安装 1、插件安装以及更换插件源 2、修改jenkins时区 二、sdkman安装&#xff08;可选&#xff09; 1、sdkman常用方法 2、sdkman常用方法演示 2.1、查看可用的jdk 2.2、下载jdk并切换版本 三、jenkins报错解决 1、下载sdkman后systemc…