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

Linux:基于环形队列的生产者消费模型

文章目录

  • 一、POSIX 信号量简介
  • 二、POSIX 信号量的操作
    • 1. 初始化与销毁
    • 2. 等待操作(P)
    • 3. 释放操作(V)
  • 三、基于环形队列的生产者消费者模型
    • 1. 环形队列原理
  • 四、信号量的简单封装
  • 五、基于 POSIX 信号量的环形队列实现
  • 六、阻塞队列与环形队列


一、POSIX 信号量简介

POSIX 信号量和 System V 信号量的作用相同,都是用于多线程或多进程间的同步,保证多个线程访问共享资源时不会发生冲突。不同的是,POSIX 信号量除了可以支持进程间同步,还可以直接用于线程间同步,因此在多线程编程中更加常见。

在这里插入图片描述

当然这个信号量的操作本身就是原子的。

常见的 API 如下:

#include <semaphore.h>// 初始化信号量
int sem_init(sem_t *sem, int pshared, unsigned int value);// 销毁信号量
int sem_destroy(sem_t *sem);// 等待信号量(P 操作)
int sem_wait(sem_t *sem);// 释放信号量(V 操作)
int sem_post(sem_t *sem);

参数说明:

  • pshared = 0 表示线程间共享,非零表示进程间共享
  • value 表示信号量初始值

二、POSIX 信号量的操作

1. 初始化与销毁

使用 sem_init() 初始化信号量时,需要指定初始值和是否进程间共享;使用 sem_destroy() 在使用完成后释放资源。

2. 等待操作(P)

调用 sem_wait() 会尝试将信号量的值减一,如果值为 0,则调用线程会被阻塞直到信号量大于 0。

3. 释放操作(V)

调用 sem_post() 会将信号量的值加一,并唤醒可能正在等待的线程,表示某个资源已经被释放。


三、基于环形队列的生产者消费者模型

在上一节我们介绍了基于 queue 的实现,它可以动态扩展容量。但在实际应用中,常常使用固定大小的缓冲区(如数组)实现一个环形队列

1. 环形队列原理

环形队列通过数组实现,利用模运算来模拟“首尾相接”的特性。

  • 环形队列在初始状态和结束状态可能相同,因此需要借助计数器或标记位来区分队列满和队列空。
  • 也可以预留一个空位来表示“满”的状态。
  • 在有了信号量后,计数的功能可以交由信号量完成,代码实现会更加简洁。
    在这里插入图片描述
    在这里插入图片描述

在这里插入图片描述


四、信号量的简单封装

在使用原始 sem_t 时,常需要重复调用初始化、等待和释放操作。为方便使用,我们可以对信号量做一个轻量级封装:

#include <iostream>
#include <semaphore.h>
#include <pthread.h>namespace SemModule
{const int defaultvalue = 1;class Sem{public:Sem(unsigned int sem_value = defaultvalue){sem_init(&_sem, 0, sem_value);}~Sem(){sem_destroy(&_sem);}void P()       // 放出资源 p 相当于资源库--{sem_wait(&_sem); // 原子的}void V()       // 回收资源 v 相当于资源库++{sem_post(&_sem);  // 原子的}private:sem_t _sem;};}

这个类提供了 P()V() 方法,分别对应 sem_waitsem_post,大大简化了信号量的调用。


五、基于 POSIX 信号量的环形队列实现

下面是一个环形队列 RingQueue 的实现,支持多生产者、多消费者。它利用两个信号量进行同步:

  • _room_sem:表示剩余空间,生产者关心
  • _data_sem:表示已有数据,消费者关心

环形队列天然可以实现生产者与消费者之间的同步与互斥关系,因为信号量本身就是原子的,但是无法满足生产/消费自身之间的互斥关系,所以需要同时使用两把互斥锁来保证多个生产者、多个消费者之间的互斥。

#pragma once#include <iostream>
#include <vector>
#include "Sem.hpp"
#include "Mutex.hpp"using namespace SemModule;
using namespace MutexModule;static const int gcap = 5;template <typename T>
class RingQueue
{
public:RingQueue(int cap = gcap): _rq(cap), _cap(cap), _blank_sem(cap), _data_sem(0), _p_step(0), _c_step(0){}void Equeue(const T &in){// 生产者// 1. 申请信号量,空位置信号量_blank_sem.P();{LockGuard lockguard(_pmutex);// 2. 生产_rq[_p_step] = in;// 3. 更新下标++_p_step;// 4. 维持环形特性_p_step %= _cap;}_data_sem.V();}void Pop(T *out){// 消费者// 1. 申请信号量,数据信号量_data_sem.P();{LockGuard lockguard(_cmutex);// 2. 消费*out = _rq[_c_step];// 3. 更新下标++_c_step;// 4. 维持环形特性_c_step %= _cap;}_blank_sem.V();}private:std::vector<T> _rq;int _cap;// 生产者Sem _blank_sem; // 生产者在乎空位置int _p_step;// 消费者Sem _data_sem; // 消费者在乎有数据位置int _c_step;// 多生产多消费之间的互斥关系维护(锁)Mutex _cmutex;Mutex _pmutex;
};

六、阻塞队列与环形队列

在 生产者–消费者模型 的实现中,资源既可以看作一个 整体,也可以拆分为 小块。

整体角度:当我们把缓冲区视为一个完整的资源池,需要用 阻塞队列 来管理整体状态(满/空),解决线程之间的等待与唤醒问题。

局部角度:当我们把缓冲区中的数据看作一个个小块时,需要用 环形队列 来组织这些小块的存放与取出,保证空间高效利用,避免资源浪费。



文章转载自:

http://H3HDbSE7.nbsfb.cn
http://aC9H9FPM.nbsfb.cn
http://JrITq296.nbsfb.cn
http://a8gDaVLB.nbsfb.cn
http://vbBtlDxi.nbsfb.cn
http://h4CgNaxx.nbsfb.cn
http://MKsyX5OX.nbsfb.cn
http://8Grnj6uU.nbsfb.cn
http://nbJwDI3l.nbsfb.cn
http://BxwYEv7P.nbsfb.cn
http://4tu6rSyz.nbsfb.cn
http://yidjPpN2.nbsfb.cn
http://oLUqpd7s.nbsfb.cn
http://c6UJPa4P.nbsfb.cn
http://M9QaLOiV.nbsfb.cn
http://PcNBaurN.nbsfb.cn
http://FtwzwNsN.nbsfb.cn
http://yCie50Bb.nbsfb.cn
http://AvMtdNoC.nbsfb.cn
http://S388kQ3O.nbsfb.cn
http://oGdDZCxk.nbsfb.cn
http://ofzLwW0t.nbsfb.cn
http://0BxFtUOP.nbsfb.cn
http://EcFhv7zR.nbsfb.cn
http://hmELvJUU.nbsfb.cn
http://61DNzbW3.nbsfb.cn
http://eHg320Y5.nbsfb.cn
http://fSfKlqnF.nbsfb.cn
http://Vq3oPUBF.nbsfb.cn
http://9Wd0RDzy.nbsfb.cn
http://www.dtcms.com/a/387117.html

相关文章:

  • Nginx 配置 Vue 项目 Hash/History 模式路由跳转错误的解决方案
  • Linux Makefile与进度条
  • 硬件驱动——I.MX6ULL裸机启动(3)(按键设置及中断设置
  • 深度学习基本模块:RNN 循环神经网络
  • 【深度学习】PixelShuffle处理操作
  • 10.1 - 遗传算法(旅行商问题C#求解)
  • Java 集合入门:从基础到实战的完整知识指南
  • 《过山车大亨3 完整版》PSXbox版下月推出 预告片赏
  • P1107题解
  • 多目标数据关联算法MATLAB实现
  • 战略推理AI Agents:组装LLM+因果推断+SHAP
  • 【CVPR 2016】基于高效亚像素卷积神经网络的实时单幅图像与视频超分辨率
  • 基于STM32的LED实战 -- 流水灯、呼吸灯、流水呼吸灯
  • 【数据结构】——队列,栈(基于链表或数组实现)
  • 任天堂官网更新!“任亏券”不支持兑换NS2专用游戏
  • 大模型数据整理器打包及填充、Flash Attention 2解析(97)
  • 48v转12v芯片48v转5v电源芯片AH7691D
  • Oracle Database 23ai 内置 SQL 防火墙启用
  • MySQL 31 误删数据怎么办?
  • 微前端面试题及详细答案 88道(09-18)-- 核心原理与实现方式
  • VBA技术资料MF362:将窗体控件添加到字典
  • 【Leetcode】高频SQL基础题--1321.餐馆营业额变化增长
  • Redis 中 Intset 的内存结构与存储机制详解
  • uniapp打包前端项目
  • cka解题思路1.32-3
  • 如何解决模型的过拟合问题?
  • 2025牛客周赛108场e题
  • 【课堂笔记】复变函数-2
  • 25、优化算法与正则化技术:深度学习的调优艺术
  • qt QCategoryAxis详解