当前位置: 首页 > news >正文

【Linux】生产消费模型实践 --- 基于信号量的环形队列

在这里插入图片描述

你送出去的每颗糖都去了该去的地方,
其实地球是圆的,
你做的好事终会回到你身上。
--- 何炅 ---

基于信号量的环形队列

  • 1 信号量
  • 2 框架构建
  • 3 代码实现
  • 4 测试运行

1 信号量

信号量本质是一个计数器,可以在初始化时对设置资源数量,进程 / 线程 可以获取信号量来对资源进行操作和结束操作可以释放信号量!
用于多进程 / 多线程 对共享数据对象的读取,它和管道有所不同,它不以传送数据为主要目的,它主要是用来保护共享资源(信号量也属于临界资源),使得资源在一个时刻只有一个进程独享。 在资源只有一个时就一把互斥锁!

信号量只能进行两种操作获取等待和释放信号,即PV操作:

  1. P(sv):我们将申请获取信号量称为P操作,申请信号量的本质就是申请获得临界资源中某块资源的使用权限,当申请成功时临界资源中资源的数目应该减去一。所以P操作的本质就是让计数器减一,如果sv的值大于零,就给它减1;如果它的值为零,就挂起该进程的执行。对应的接口为,使用很简单:
    #include <semaphore.h>
    //阻塞等待获取
    int sem_wait(sem_t *sem);
    //只进行一次获取,非阻塞等待
    int sem_trywait(sem_t *sem);
    //时间片内进行等待,超出就退出阻塞!
    int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
  2. V(sv):我们将释放信号量称为V操作,释放信号量的本质就是归还临界资源中某块资源的使用权限,当释放成功时临界资源中资源的数目就应该加一。所以V操作本质就是让计数器加一,如果有其他进程 / 线程因等待sv而被挂起,就发送信号让它恢复运行,如果没有进程 / 线程因等待信号量而挂起,就给他加1。对应接口为:
    #include <semaphore.h>
    //释放获取的信号量
    int sem_post(sem_t *sem);
    

PV操作都是原子的,不用担心线程安全!此外信号量初始化和销毁的接口是:

  1. 信号量初始化:
    #include <semaphore.h>
    int sem_init(sem_t *sem, int pshared, unsigned int value);
    
    参数分别为:
    • sem_t *sem:传入信号量的地址
    • pshared:传入0值表示线程间共享,传入非零值表示进程间共享。
    • value:信号量的初始值(计数器的初始值)。
  2. 信号量销毁:
    #include <semaphore.h>
    int sem_destroy(sem_t *sem);
    

2 框架构建

  1. 环形队列的成员变量

    • 线性容器vector模拟环形队列
    • 最大容量 int _max_step
    • 消费者位置 _c_step 与 生产者位置 _p_step
    • 两个信号量来表示生产与消费的剩余容量
      sem_t _data_sem : 当前有多少数据
      sem_t _space_sem: 当前剩余空间还有多少
  2. 构造函数初始化

    • 最大容量需要给值初始化
    • 两个初始位置都为 0
    • 信号量初始化 sem_init() 数据为 0 ,空间为 最大容量
  3. Push接口用来加入数据

    • 首先需要申请信号量 P 来对空间信号量进行获取 sem_wait (&sem_t _space_sem)(申请信号量是原子的)
      获取信号量的本质是对资源 –
    • 生产进行插入 , 对应下标向后移动 , 注意不能越界
    • 最后进行释放信号量 V 来对资源信号量进行释放 sem_post()
      释放信号量的本质是对资源 ++
  4. Pop接口用来获取数据

    • 首先需要申请信号量 P 来对资源信号量进行获取 sem_wait (&sem_t _space_sem)(申请信号量是原子的)
      获取信号量的本质是对资源 –
    • 获取队列资源,并进行释放, 对应下标向后移动 , 注意不能越界
    • 最后进行释放信号量 V 来对空间信号量进行释放 sem_post()
      释放信号量的本质是对资源 ++
  5. 多生产多消费改造:多个生产 / 消费线程存在 消费对消费 生产对生产的问题!

    • 信号量保证了单生产单消费中,两个线程可以通过信号量来保证不会出现访问越界 / 访问重叠的问题!
    • 多线程的情况下可能会发生访问同一位置的可能,获取到信号量之后由于中间的处理是临界区,可能会发生线程的切换,就会导致对同一位置进行处理,进而发生问题!
    • 为了保证线程安全,需要两把锁,分别管理生产者和消费者
    • 锁的处理:
      • 获取信号量之后再进行加锁,获取信号量是原子的,先申请信号量可以保证多个线程在获取中进行排队等待。
      • 如果先加锁,就只能使一个线程进入到获取信号量的队列中,效率低(电影院先买票在排队 ,先排队再买票)

6.为什么信号量不加条件判断?:
在环形队列的实现中,没有使用条件变量,像阻塞队列一样进行条件的判断 而是直接来不管三七二十一进行获取信号量,因为信号量本身就是判断条件,信号量是用来描述内部资源的多少的,是原子的!本质是一个计数器 通过预订机制来保证内部资源的合理使用,当信号量的资源数量为1时和锁时等价的!

3 代码实现

#pragma once#include <vector>
#include <semaphore.h>const int default_cap = 5;template <class T>
class RingQueue
{
public:RingQueue(int max_cap = default_cap) : _rq(max_cap), _max_cap(max_cap), _p_step(0), _c_step(0){// 信号量初始化sem_init(&_space_sem, 0, _max_cap);sem_init(&_data_sem, 0, 0);//锁进行初始化pthread_mutex_init(&_c_mtx , nullptr);pthread_mutex_init(&_p_mtx , nullptr);}// 获取信号量void P(sem_t &sp){sem_wait(&sp);}// 释放信号量void V(sem_t &sp){sem_post(&sp);}// 插入操作void Push(const T &t){// 获取空间信号量 --P(_space_sem);//临界区上锁pthread_mutex_lock(&_p_mtx );_rq[_p_step] = t;_p_step++;_p_step %= _max_cap;//解锁pthread_mutex_unlock(&_p_mtx);// 释放信号量 ++V(_data_sem);}// 获取操作void Pop(T *t){// 获取资源信号量P(_data_sem);pthread_mutex_lock(&_c_mtx);*t = _rq[_c_step];_c_step++;_c_step %= _max_cap;pthread_mutex_unlock(&_c_mtx);// 释放信号量V(_space_sem);}~RingQueue(){// 销毁对应信号量!sem_destroy(&_space_sem);sem_destroy(&_data_sem);//锁进行释放pthread_mutex_destroy(&_c_mtx);pthread_mutex_destroy(&_p_mtx);}private:// 底层线性结构,模拟环形队列std::vector<T> _rq;// 最大容量int _max_cap;// 生产者/消费者 下标int _p_step;int _c_step;// 空间/资源 信号量sem_t _space_sem;sem_t _data_sem;// 生产 / 消费 锁pthread_mutex_t _p_mtx;pthread_mutex_t _c_mtx;};

4 测试运行

我们来做一些简单测试,我们设计了Task类,用于执行加法操作。它包含两个整型参数_x_y,并提供方法来执行加法并获取结果。通过重载括号运算符,Task对象可以被直接调用以执行计算。此外,类还提供了调试信息和结果输出的功能。

我写了一段代码段用于测试。在该测试中:定义了两个线程函数ConsumerProductor,分别模拟消费者和生产者行为:

  1. Consumer线程不断从环形队列中取出Task对象,执行其操作,并打印消费结果。
  2. Productor线程则持续生成新的Task对象并将其放入队列中,同时打印出生产信息。

主函数main中创建了一个容量为5的RingQueue<Task>实例,并启动了两个线程。pthread_create用于创建线程,pthread_join确保主线程等待子线程执行完毕。通过这种方式,我们验证了环形队列在多线程环境下的线程安全性和功能正确性。

#include <iostream>
#include "RingQueue.hpp"
#include <pthread.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include "Task.hpp"void *Consumer(void *args)
{RingQueue<Task> *rq = static_cast<RingQueue<Task> *>(args);srand(time(nullptr) ^ getpid());while (true){// 不断的进行获取Task data ;rq->Pop(&data);data();std::cout << "Consumer 消费者消费 -> " << data.result() << std::endl;sleep(1);}
}
void *Productor(void *args)
{RingQueue<Task> *rq = static_cast<RingQueue<Task> *>(args);srand(time(nullptr) ^ getpid());while (true){// 不断的进行写入int num1 = rand() % 10;usleep(1000);int num2 = rand() % 10;Task t(num1 , num2);rq->Push(t);std::cout << "Productor 生产者生产 -> " << t.debug() << std::endl;usleep(10000);}
}int main()
{// 环形队列RingQueue<Task> rq(5);// 使用两个线程来测试pthread_t t1, t2;pthread_create(&t1, nullptr, Consumer, &rq);pthread_create(&t2, nullptr, Productor, &rq);pthread_join(t1, nullptr);pthread_join(t2, nullptr);
}

运行效果:
在这里插入图片描述
很好的完成了任务!!!

相关文章:

【Linux】生产消费模型实践 --- 基于信号量的环形队列

你送出去的每颗糖都去了该去的地方&#xff0c; 其实地球是圆的&#xff0c; 你做的好事终会回到你身上。 --- 何炅 --- 基于信号量的环形队列 1 信号量2 框架构建3 代码实现4 测试运行 1 信号量 信号量本质是一个计数器&#xff0c;可以在初始化时对设置资源数量&#xf…...

Science Robotics 与蜜蜂群互动的蜂窝型机器人系统

蜜蜂&#xff0c;如黄蜂&#xff0c;蚂蚁和其他社会昆虫&#xff0c;建立大型自组织群体&#xff0c;通常被解释为自我调节的“超有机体”。这些超生物是生态系统的重要稳定剂&#xff0c;因此被认为是“关键物种”。例如&#xff0c;蜜蜂群落通过觅食授粉服务的生态效应对陆地…...

Vue 计算属性:优雅地处理数据逻辑

在 Vue.js 中&#xff0c;计算属性&#xff08;Computed Properties&#xff09;是一种非常实用的功能&#xff0c;它允许我们根据组件的响应式依赖进行缓存和派生状态。计算属性可以让我们以声明式的方式编写复杂的逻辑&#xff0c;而不必担心性能问题。 什么是计算属性&…...

C++中`union`

文章目录 C中的union什么是union&#xff1f;定义union示例一输出结果&#xff1a; 示例二修正后的代码解释输出结果结论 union的特性匿名union示例 union和struct的区别1. 内存布局2. 同时访问3. 用途 union和class的区别1. 数据成员2. 功能性3. 适用场景 在C编程中&#xff0…...

Linux——网络(1)

一、IPC&#xff08;进程间通信方式&#xff09; IPC&#xff1a;Inter Process Communication 共享内存&#xff08;最高效的进程间通信方式&#xff09; 虚拟地址 mmu(memory management unit ) 共享内存: 1.是一块&#xff0c;内核预留的空间 2.最高效的…...

【五】阿伟开始学Kafka

阿伟开始学Kafka 概述 人生若只如初见&#xff0c;阿伟心里回想起了第一次和Kafka见面的场景&#xff0c;记忆虽然已经有些模糊&#xff0c;但是感觉初次见面是美好的。积累了一些实战经验之后&#xff0c;阿伟感觉不能再是面对百度开发了&#xff0c;于是决心系统的学习一下Ka…...

Java—Arrays api

public static String toString(数组) //把数组拼接成一个字符串 public static int binarySearch(数组&#xff0c;查找的元素) //二分查找法查找元素 public static int[] copyOf(原数组,新数组长度) //拷贝数组 public st…...

Java - 基数排序算法介绍、应用场景和示例代码

概述 基数排序&#xff08;Radix Sort&#xff09;是一种非比较型整数排序算法&#xff0c;适用于整数或固定长度的字符串排序。它的基本思想是将待排序的元素分为多个关键字进行排序&#xff0c;通常从最低位&#xff08;最低有效位&#xff0c;Least Significant Digit, LSD…...

Django 后端架构开发:文件云存储,从本地存储到腾讯COS桶集成

⭐ Django 后端架构开发&#xff1a;文件云存储&#xff0c;从本地存储到腾讯COS桶集成 目录 ☁️ 文件云存储 - 项目使用云存储&#x1f4bb; 文件云存储 - 项目中使用本地存储&#x1f4dd; 文件云存储 - 概述和创建项目&#x1f310; 腾讯COS桶 - 概述&#x1f4da; 腾讯CO…...

【系统分析师】-综合知识-计算机网络与信息安全

1、要对消息明文进行加密传送&#xff0c;当前通常使用的加密算法是 报文认证算法&#xff1a;数字摘要 RSA 非对称加密&#xff0c;一般不用于明文 MD5 数字摘要 SHA-1 数字摘要&#xff0c;160位的消息摘要 HMAC 以一个密钥和一个消息为输入&#xff0c;生成一个消息摘要作…...

C++ | Leetcode C++题解之第363题矩形区域不超过K的最大数值和

题目&#xff1a; 题解&#xff1a; class Solution { public:int maxSumSubmatrix(vector<vector<int>> &matrix, int k) {int ans INT_MIN;int m matrix.size(), n matrix[0].size();for (int i 0; i < m; i) { // 枚举上边界vector<int> sum(…...

python动画:场景的线性变换展示

一&#xff0c;主函数 LinearTransformationScene 是 Manim 中用于展示线性变换的场景类。它通过在一幅背景和前景平面上展示向量和变换&#xff0c;帮助理解线性代数中的概念。 LinearTransformationScene(include_background_planeTrue, include_foreground_planeTrue, ba…...

HBase体系架构与环境搭建

这里写目录标题 一、常见的NoSQL数据库二、HBase的体系架构和表结构三、搭建HBasa环境1.本地模式2.伪分布模式全分布模式HA模式 一、常见的NoSQL数据库 NoSQL数据库的说明与定义 NoSQL是一种不同于关系数据库的数据库管理系统设计方式&#xff0c;是对非关系型数据库的统称。它…...

海思SD3403/SS928V100开发(16)Tsensor驱动开发

1. 前言 由于需要检测SD3403芯片内部实时温度,需要开发Tsensor传感器驱动和应用 查看手册发现SD3403内部有三个Tsensor传感器 可以参考之前我写的35系列平台Tsensor驱动开发记录 海思35系列平台Tsensor驱动开发(1)驱动编写_t sensor-CSDN博客 海思35系列平台Tsensor驱动…...

JVM类加载机制—JVM类加载过程

一、概述 代码编译后&#xff0c;就会生成JVM&#xff08;Java虚拟机&#xff09;能够识别的二进制字节流文件&#xff08;*.class&#xff09;。而JVM把Class文件中的类描述数据从文件加载到内存&#xff0c;并对数据进行校验、转换解析、初始化&#xff0c;使这些数据最终成…...

可变参数模板与包装器

抱歉&#xff1a;铁汁们&#xff0c;最近在做兼职&#xff0c;积累社会经验&#xff0c;多有拖欠&#xff0c;请多多包涵&#xff08;抱拳&#xff09; 引子&#xff1a;接上回我们讲了C11的几种新增&#xff0c;今天就来接着讲C11中比较有用的二个东西可变参数模板与包装器。…...

工业控制常用“对象“数据类型汇总(数据结构篇)

合理巧妙的数据结构会大大简化项目的编程工作量,所以任何项目前期第一步应该是设计巧妙的数据结构、封装对象属性。这样会使我们的编程快捷和高效。这篇博客作为数据类型汇总,会不间断更新。 1、普通电机轴对象 2、普通电机轴对象(详细结构变量) TYPE "udtMotorAxis&q…...

优雅处理枚举前端丢失大Long精度问题

1. 枚举-json处理&#xff08;前端 <> 后端 <> 数据库&#xff09; 前端传递 枚举code 后端响应 枚举code 表里存储 枚举code 内存处理 枚举对象 Getter AllArgsConstructor JsonFormat(shape JsonFormat.Shape.OBJECT) public enum SexEnum {MALE(0, "男&…...

【c/c++】 学习ector 容器笔记

c/c 学习ector 容器笔记 int 型的 vector 容器应该使用什么类型的索引&#xff1f; 对于 int 型的 vector 容器&#xff0c;应该使用 size_t 类型的索引。size_t 是一个无符号整数类型&#xff0c;它在标准库中广泛用于表示大小和索引。它足够大&#xff0c;可以表示任何标准…...

DN专业3D图形制作软件win/mac软件安装下载(附下载链接)

目录 一、软件概述 1.1 Adobe DN简介 1.2 Windows/Mac系统要求 Windows系统&#xff1a; Mac系统&#xff1a; 二、安装步骤 2.1 下载与解压 2.2 安装程序 2.3 启动软件 三、使用教程 3.1 界面介绍 3.2 创建和编辑3D内容 3.3 合成与渲染 四、高级技巧与注意事项 …...

VSCode搭建Hzero(SpringCloud架构)后端开发调试环境

正常情况下我们使用IDEA开发Hzero&#xff0c;但是有的公司是不允许破解或者使用IDEA的&#xff0c;此时可以使用eclipse来替代也是可以的&#xff0c;最近尝试使用VSCode来开发调试发现了一些问题其中最大的问题是Vscdoe在绝大多数情况下是不能直接运行Hzero&#xff0c;使用插…...

【C++】OJ习题(初阶)

&#x1f680;个人主页&#xff1a;奋斗的小羊 &#x1f680;所属专栏&#xff1a;C 很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~ 目录 &#x1f4a5;1、字符串&#x1f4a5;1.1 字符串相加&#x1f4a5;1.2 验证回文字符串&#x1f4a5;1.3 反转…...

6.4K+ Star!一个强大的本地知识库问答系统,支持多格式文件和跨语言检索,为企业提供高效、安全的数据洞察……

https://github.com/netease-youdao/QAnything 【阅读原文】跳转Github项目 转自AIGC创想者 项目简介 QAnything 是一个基于本地知识库的问答系统&#xff0c;它能够理解和回答基于任何类型文件的问题。 QAnything支持的文件格式非常广泛&#xff0c;包括PDF、Word、PPT、XL…...

mvn编译的时候出现Perhaps you are running on a JRE rather than a JDK 解决方法

目录 1. 问题所示2. 原理分析3. 解决方法1. 问题所示 mvn编译的时候出现如下问题: [ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.13.0:compile (default-compile) on project yudao...

React原理之Fiber详解

前置文章&#xff1a; React原理之 React 整体架构解读React原理之整体渲染流程 -----读懂这一篇需要对 React 整体架构和渲染流程有大致的概念 &#x1f60a;----- 在React原理之 React 整体架构解读中&#xff0c;简单介绍了 Fiber 架构&#xff0c;也了解了 Fiber 节点的…...

远离“优越感”陷阱,拥抱美好人生

在人生的漫长旅程中,我们不断地与他人相遇、相知、相交,在各种关系中寻找温暖、支持与成长。然而,并非所有的关系都如我们所愿,有些关系甚至可能成为我们前进道路上的阻碍。正如我们所知,唯利是图者不可交,但有一种关系比索要金钱更值得警惕,那就是找你索取满足感的关系…...

Redis的线程模型

Redis作为一种基于内存的高性能键值对数据库&#xff0c;其线程模型和IO模型是实现高性能的关键因素。以下将详细探讨Redis的线程与IO模型&#xff0c;内容不少于2000字。 一、Redis的线程模型 Redis的线程模型是理解其高性能的重要基础。在Redis的发展过程中&#xff0c;其线…...

ubuntu24.04安装nginx1.24

ubuntu安装nginx 更新包索引 sudo apt update安装nginx sudo apt install nginx确认安装成功并检查Nginx版本 nginx -v启动Nginx服务 sudo systemctl start nginx设置Nginx开机自启 sudo systemctl enable nginx在浏览器中访问 http://<your_server_IP> 来确认Nginx…...

一款好看的WordPress REST API 主题

介绍&#xff1a; 主题特色&#xff1a; 使用Nuxtjs WordPress Rest Api 实现前后端分离&#xff0c;可完成多端部署&#xff1b; 主题支持自动切换黑夜模式。 使用说明&#xff1a; service 目录为wordpress主题文件&#xff0c;需要拷贝到wordpress主题目录下&#xff0…...

《5G 与区块链融合:智能城市服务质量的飞跃》

在科技飞速发展的时代&#xff0c;5G 技术的普及正以前所未有的速度改变着我们的生活&#xff0c;而区块链技术的兴起也为各领域带来了创新的解决方案。当这两种前沿技术相互结合&#xff0c;将为智能城市的发展注入强大动力&#xff0c;显著提升服务质量&#xff0c;开创更加便…...

哪里有做php网站免费教程/网站推广沈阳

Hexo更改主题后启动服务器&#xff0c;界面显如下字符: extends includes/layout.pug block content include includes/recent-posts.pug include includes/partial 解决方案: 执行如下命令 npm install --save hexo-renderer-jade hexo-generator-feed hexo-generator-sit…...

wordpress插件连不上/网站策划运营

培训主题 消费系统培训 消费机功能基本操作介绍 CM20集消费机 出纳机 补贴机功能于一体 机器的操作 分为机器操作和软件操作两大块 其中可以通过机器操作来实现消费机 出纳机 补贴机之间的切换 软件界面简洁清晰 操作方便 功能齐全 系统稳定可靠 大大提升了管理效率 机器硬件核…...

常州建站软件/seo推广技术培训

文章目录Java锁synchronized关键字学习系列之偏向锁升级无锁偏向锁原理批量重偏向和批量撤销偏向锁升级偏向锁升级轻量级锁偏向锁升级重量级锁参考源代码Java锁synchronized关键字学习系列之偏向锁升级 前面几篇博文已经简单介绍了偏向锁了。《Java锁synchronized关键字学习系…...

网站建设要注意/网络营销和网络推广

上一篇讲到游戏运作的原理是非常简单的,实现一个五子棋很可能只需要使用操作系统提供的原生开发环境,简单的做一些代码实现即可完成,我自己在大学的时候就找到了一个一步一步教你使用Windows API开发一个五子棋游戏的教程,需要用到的也只需要安装一个visual studio顺便安装…...

变更股东怎样在工商网站做公示/百度收录入口提交查询

1722. 执行交换操作后的最小汉明距离 思路&#xff1a; 并查集合并联通分量统计每个联通分量的不同的数字的个数 class UnionFind {int[] f;int N;public UnionFind(int n) {N n10;f new int[N];for(int i0;i<N;i) f[i] i;}public UnionFind() {this((int)5e5);}int fi…...

网站设计与规划/搜外友链

官方文档&#xff1a; 高德地图API官网 高德地图2.0参考手册 高德地图JS API 2.0 示例 在项目中使用 vue-amap 高德地图JSAPI在Vue框架下使用 高德地图在线 JS API 示例 一、账号准备 首先&#xff0c;需要注册并登录高德地图开放平台&#xff0c;申请密钥。操作指引&#x…...