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

【Linux | C++ 】基于环形队列的多生产者多消费者模型(Linux系统下C++ 代码模拟实现)

在这里插入图片描述

阅读导航

  • 引言
  • 一、生产者消费者模型
  • 二、环形队列简介
  • 三、基于环形队列的生产者消费者模型(C++ 代码模拟实现)
    • ⭕Makefile文件
    • ⭕ . h 头文件
      • ✅sem.hpp
      • ✅ringQueue.hpp
    • ⭕ . cpp 文件
      • ✅testMain.cpp
  • 温馨提示

引言

在上一篇文章中,我们深入探讨了Linux操作系统中的POSIX信号量,这是一个强大的同步机制,用于协调进程或线程对共享资源的访问。通过对信号量的深入理解和应用,我们学习了如何有效地解决并发编程中的竞争条件,确保程序的稳定性和效率。随着并发编程技术的不断深入,理解和掌握更多同步模型对于开发高性能、可靠的软件系统变得尤为重要。因此,本篇文章将继续我们的并发编程之旅,引入一个经典且实用的同步模型——基于环形队列的生产者消费者模型

在本文中,我们将详细探讨基于环形队列的生产者消费者模型的设计和实现。我们将介绍环形队列的数据结构,分析生产者和消费者之间的同步机制,探索如何利用前文提到的POSIX信号量以及其他同步工具(如互斥锁)来实现生产者和消费者之间高效、安全的数据交换。通过具体的代码示例和案例分析,读者将能够深入理解生产者消费者模型的工作原理,掌握如何在实际项目中设计和实现基于环形队列的高效同步模型。

探索基于环形队列的生产者消费者模型,不仅能够加深我们对并发编程同步机制的理解,还能够提升我们解决实际问题的能力。让我们一起继续并发编程的探索之旅,解锁更多的编程技巧和知识。

一、生产者消费者模型

生产者消费者模型是并发编程中一个经典且重要的问题模型,它描述了两类主体——生产者(Producer)和消费者(Consumer)在并发环境下对共享资源(通常是缓冲区或队列)的访问模式。生产者负责生成数据并将其放入缓冲区,而消费者则从缓冲区取出数据进行处理。该模型的核心在于解决生产者和消费者之间的同步与通信问题,保证数据在生产和消费时的一致性和可用性,同时避免资源的冲突和浪费。对于希望深入了解生产者消费者模型的读者,我们在之前的内容中有所介绍——链接:⭕生产者消费者模型

通过上述简介,希望读者能够对生产者消费者模型有一个初步的认识和理解。在并发编程的实践中,该模型不仅是一个常见的问题场景,也提供了一种思考并发问题的方法论,对于提高编程技能和系统设计能力都有重要意义。

二、环形队列简介

环形队列是一种固定大小的、使用数组实现的队列数据结构,特别在于其首尾相连的循环特性。这种结构允许当数组达到其容量上限时,新加入的元素可以放置在数组的开始位置(如果那里有空位)。环形队列的这一设计使得它在空间利用和操作效率上具有显著优势,尤其适用于有固定缓冲区需求的场景。

🚩主要特点包括:

  • 固定大小:一旦创建,队列的大小就固定不变。
  • 高效操作:入队和出队操作都非常高效,因为它们仅涉及指针的简单移动。
  • 两个指针:使用头指针和尾指针来分别追踪队列的第一个和最后一个元素。

环形队列广泛应用于操作系统、网络通信、生产者消费者模型等多个领域,特别是在需要高效管理固定缓冲区资源的场合。实现环形队列时,关键在于正确管理头尾指针的位置,并准确判断队列的空或满状态。
在这里插入图片描述

三、基于环形队列的生产者消费者模型(C++ 代码模拟实现)

⭕Makefile文件

ring_queue:testMain.ccg++ -o $@ $^ -std=c++11 -lpthread
.PHONY:clean
clean:rm -f ring_queue

这段代码是一个Makefile脚本,用于编译和清理一个名为ring_queue的项目。

⭕ . h 头文件

✅sem.hpp

// 防止头文件重复包含的预处理指令。
#ifndef _SEM_HPP_
#define _SEM_HPP_// 引入输入输出流库,虽然在此代码中未直接使用,可能为后续扩展预留。
#include <iostream>
// 引入POSIX信号量的头文件。
#include <semaphore.h>// 定义一个类 Sem。
class Sem
{
public:// 构造函数,接收一个整数value作为信号量的初始值。Sem(int value){// 初始化信号量,其中&sem_是信号量对象的地址,// 0表示信号量是当前进程的局部信号量,// value是信号量的初始值。sem_init(&sem_, 0, value);}// p操作,也称为wait操作,用于减少信号量的值。// 如果信号量的值为0,则调用此方法的线程将阻塞,直到信号量的值大于0。void p(){sem_wait(&sem_);}// v操作,也称为signal操作,用于增加信号量的值。// 如果有其他线程因为等待此信号量而阻塞,则它们中的一个将被唤醒。void v(){sem_post(&sem_);}// 析构函数,用于销毁信号量。~Sem(){sem_destroy(&sem_);}private:// 私有成员变量,存储信号量对象的实例。sem_t sem_;
};// 预处理指令的结束标志。
#endif

这个Sem类提供了简单的接口来进行信号量的基本操作:初始化(构造函数)、等待(p方法)、信号(v方法)和销毁(析构函数)。通过这个类,可以更方便地在C++项目中使用POSIX信号量进行同步操作

✅ringQueue.hpp

// 防止头文件重复包含的预处理指令。
#ifndef _Ring_QUEUE_HPP_
#define _Ring_QUEUE_HPP_// 引入所需的头文件。
#include <iostream>
#include <vector>
#include <pthread.h>
#include "sem.hpp"// 定义一个全局常量作为队列的默认大小。
const int g_default_num = 5;// 定义一个模板类RingQueue,用于实现环形队列。
template<class T>
class RingQueue
{
public:// 构造函数,参数default_num指定队列的大小,默认为g_default_num。RingQueue(int default_num = g_default_num): ring_queue_(default_num), num_(default_num),c_step(0),p_step(0),space_sem_(default_num), // 初始化空间信号量,表示可用空间数量。data_sem_(0) // 初始化数据信号量,表示队列中的数据项数量。{pthread_mutex_init(&clock, nullptr); // 初始化消费者互斥锁。pthread_mutex_init(&plock, nullptr); // 初始化生产者互斥锁。}// 析构函数,销毁互斥锁。~RingQueue(){pthread_mutex_destroy(&clock);pthread_mutex_destroy(&plock);}// push方法,生产者调用,向队列中添加元素。void push(const T &in){space_sem_.p(); // 等待有空间可写。pthread_mutex_lock(&plock); // 获取生产者互斥锁。ring_queue_[p_step++] = in; // 将元素添加到队列中。p_step %= num_; // 环形逻辑,如果到达末尾则回到开始。pthread_mutex_unlock(&plock); // 释放生产者互斥锁。data_sem_.v(); // 增加数据信号量,表示有新数据可读。}// pop方法,消费者调用,从队列中取出元素。void pop(T *out){data_sem_.p(); // 等待有数据可读。pthread_mutex_lock(&clock); // 获取消费者互斥锁。*out = ring_queue_[c_step++]; // 从队列中取出元素。c_step %= num_; // 环形逻辑,如果到达末尾则回到开始。pthread_mutex_unlock(&clock); // 释放消费者互斥锁。space_sem_.v(); // 增加空间信号量,表示有空间可写。}private:std::vector<T> ring_queue_; // 使用vector存储队列元素。int num_; // 队列的大小。int c_step; // 消费者在队列中的当前位置。int p_step; // 生产者在队列中的当前位置。Sem space_sem_; // 控制队列空间的信号量。Sem data_sem_; // 控制队列中数据的信号量。pthread_mutex_t clock; // 消费者互斥锁。pthread_mutex_t plock; // 生产者互斥锁。
};#endif   // 预处理指令的结束标志。

这个环形队列的实现利用信号量space_sem_data_sem_来控制队列的空间和数据,确保生产者不会在队列满时添加元素,消费者不会在队列空时尝试取出元素。同时,通过两个互斥锁clockplock分别保护消费者和生产者的操作,防止并发环境下的数据竞争问题。这样的设计使得RingQueue既能高效地管理数据,又能保证线程安全

⭕ . cpp 文件

✅testMain.cpp

// 包含RingQueue类的头文件。
#include "ringQueue.hpp"
#include <cstdlib> // 包含标准库,用于rand()等函数。
#include <ctime>   // 用于time()函数。
#include <sys/types.h> // 包含类型定义,例如pid_t。
#include <unistd.h>    // 包含各种常量和类型,并声明了各种函数,例如sleep()和getpid()。// 消费者线程的工作函数。
void *consumer(void *args)
{RingQueue<int> *rq = (RingQueue<int> *)args; // 将传入的参数转换为RingQueue指针。while(true){sleep(1); // 休眠1秒,模拟处理时间。int x;rq->pop(&x); // 从环形队列中取出一个元素。// 打印消费信息,包括消费的值和当前线程ID。std::cout << "消费: " << x << " [" << pthread_self() << "]" << std::endl;}
}// 生产者线程的工作函数。
void *productor(void *args)
{RingQueue<int> *rq = (RingQueue<int> *)args; // 将传入的参数转换为RingQueue指针。while(true){int x = rand() % 100 + 1; // 生成一个1到100之间的随机数。// 打印生产信息,包括生产的值和当前线程ID。std::cout << "生产: " << x << " [" << pthread_self() << "]" << std::endl;rq->push(x); // 将生成的随机数放入环形队列中。}
}int main()
{srand((uint64_t)time(nullptr) ^ getpid()); // 设置随机数种子,确保每次运行结果不同。RingQueue<int> *rq = new RingQueue<int>(); // 创建一个RingQueue对象。pthread_t c[3], p[2]; // 定义线程ID数组,3个消费者和2个生产者。// 创建消费者线程。pthread_create(&c[0], nullptr, consumer, (void*)rq);pthread_create(&c[1], nullptr, consumer, (void*)rq);pthread_create(&c[2], nullptr, consumer, (void*)rq);// 创建生产者线程。pthread_create(&p[0], nullptr, productor, (void*)rq);pthread_create(&p[1], nullptr, productor, (void*)rq);// 等待所有线程完成。for(int i = 0; i < 3; i++) pthread_join(c[i], nullptr);for(int i = 0; i < 2; i++) pthread_join(p[i], nullptr);return 0; // 程序结束。
}

这段代码展示了如何使用前面定义的RingQueue类来创建一个多生产者-多消费者模型。在这个模型中,生产者生成随机数并将其放入环形队列,而消费者从队列中取出这些数字并处理它们

首先通过srand()设置随机数种子,以确保每次程序运行时生成的随机数序列不同。然后,它创建了一个RingQueue<int>对象,用于存储生产者线程生成的整数。

接着,代码创建了3个消费者线程和2个生产者线程。每个线程都被分配了一个工作函数:生产者调用productor函数,而消费者调用consumer函数。这些线程通过pthread_create函数创建,并将RingQueue对象作为参数传递给它们的工作函数。

最后,main函数使用pthread_join等待所有线程完成,以确保程序在所有线程都执行完毕后才退出。

温馨提示

感谢您对博主文章的关注与支持!如果您喜欢这篇文章,可以点赞、评论和分享给您的同学,这将对我提供巨大的鼓励和支持。另外,我计划在未来的更新中持续探讨与本文相关的内容。我会为您带来更多关于Linux以及C++编程技术问题的深入解析、应用案例和趣味玩法等。如果感兴趣的话可以关注博主的更新,不要错过任何精彩内容!

再次感谢您的支持和关注。我们期待与您建立更紧密的互动,共同探索Linux、C++、算法和编程的奥秘。祝您生活愉快,排便顺畅!
在这里插入图片描述

相关文章:

【Linux | C++ 】基于环形队列的多生产者多消费者模型(Linux系统下C++ 代码模拟实现)

阅读导航 引言一、生产者消费者模型二、环形队列简介三、基于环形队列的生产者消费者模型&#xff08;C 代码模拟实现&#xff09;⭕Makefile文件⭕ . h 头文件✅sem.hpp✅ringQueue.hpp ⭕ . cpp 文件✅testMain.cpp 温馨提示 引言 在上一篇文章中&#xff0c;我们深入探讨了…...

【Docker】Docker存储卷

文章目录 一、什么是存储卷二、为什么需要存储卷三、存储卷分类四、管理卷Volume创建卷方式一&#xff1a;Volume 命令操作方式二&#xff1a;-v 或者--mount 指定方式三&#xff1a;Dockerfile 匿名卷 操作案例Docker 命令创建管理卷Docker -v 创建管理卷Docker mount 创建管理…...

基于python的租车管理平台/汽车租赁网站

功能介绍 平台采用B/S结构&#xff0c;后端采用主流的Python语言进行开发&#xff0c;前端采用主流的Vue.js进行开发。 整个平台包括前台和后台两个部分。 前台功能包括&#xff1a;首页、详情页、用户中心、家政入驻模块。后台功能包括&#xff1a;总览、车辆管理、分类管理…...

【JVM】双亲委派机制

&#x1f4dd;个人主页&#xff1a;五敷有你 &#x1f525;系列专栏&#xff1a;JVM ⛺️稳中求进&#xff0c;晒太阳 双亲委派机制 在Java中如何使用代码的方式去主动加载一个类呢&#xff1f; 方式1&#xff1a;使用Class.forName方法&#xff0c;使用当前类的类加载…...

分布式id实战

目录 常用方式 特征 潜在问题 信息安全 高性能 UUID 雪花算法 数据库生成 美团Leaf方案 Leaf-segment 数据库方案 Leaf-snowflake 方案 常用方式 uuid雪花算法数据库主键 特征 全局唯一趋势递增信息安全 潜在问题 信息安全 如果id连续递增, 容易被爬虫, 批量下…...

深入了解 SOCKS5 代理、代理 IP 和 HTTP

在网络通信和数据传输中&#xff0c;代理服务器扮演着至关重要的角色。本文将深入探讨 SOCKS5 代理、代理 IP 和 HTTP&#xff0c;揭示它们的工作原理、应用场景以及优缺点。 1. SOCKS5 代理 SOCKS&#xff08;Socket Secure&#xff09;是一种网络协议&#xff0c;允许客户端…...

外包干了3个多月,技术退步明显。。。。

先说一下自己的情况&#xff0c;本科生&#xff0c;19年通过校招进入广州某软件公司&#xff0c;干了接近3年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能测试…...

Unity之闪电侠大战蓝毒兽(简陋的战斗系统)

目录 &#x1f3a8;一、创建地形 &#x1f3ae;二、创建角色 &#x1f3c3;2.1 动画 &#x1f3c3;2.2 拖尾 &#x1f3c3;2.3 角色控制 ​&#x1f3c3;2.4 技能释放 &#x1f3c3;2.5 准星 &#x1f4f1;三、创建敌人 &#x1f432;3.1 选择模型 &#x1f432;3.…...

C# 菜鸟级别有关于redis的使用

public IActionResult Index() { ConnectionMultiplexer _conn ConnectionMultiplexer.Connect("127.0.0.1:6379");//初始化 var database _conn.GetDatabase(7);//指定连接的库 0 RedisHelper redisHelper new Redi…...

AlexNet的出现推动深度学习的巨大发展

尽管AlexNet&#xff08;2012&#xff09;的代码只比LeNet&#xff08;1998&#xff09;多出几行&#xff0c;但学术界花了很多年才接受深度学习这一概念&#xff0c;并应用其出色的实验结果。 AlexNet&#xff08;由Alex Krizhevsky、Ilya Sutskever和Geoffrey Hinton共同设计…...

2024面试offer收割宝典字节篇

1.IO 模型有哪些,讲讲你理解的 nio ,他和 bio,aio 的区别是啥, 谈谈 reactor 模型。 IO 模型主要包括以下几种:1. 阻塞 I/O (BIO): 当一个线程调用 read() 或 write() 系统调用时,如果数据没有准备好或者缓冲区已满,则该线程会被操作系统阻塞,直到有数据可读或写入完…...

冒泡排序及其优化

冒泡排序 int[] arr {1,3,2,9,4,7,2,8};//比较多少轮(n个数字比较n-1次)for(int i0,n arr.length;i<n-1;i) {//每轮比较多少次(n-1-i次)for(int j 0;j<n-1-i;j) {//两两比较if(arr[j] > arr[j1]) { //比较结果为升序排列&#xff0c;如果想要降序排列结果将 >…...

【医学大模型 补全主诉】BioGPT + LSTM 自动补全医院紧急部门主诉

BioGPT LSTM 自动补全医院紧急部门主诉 问题&#xff1a;针对在紧急部门中自动补全主诉的问题子问题1: 提高主诉记录的准确性子问题2: 加快主诉记录的速度子问题3: 统一医疗术语的使用子问题4: 减少打字错误和误解子问题5: 提高非特定主诉的处理能力 解法数据预处理神经网络方…...

HCIE-Datacom证书有效期多久?HCIE考试有哪些内容?

如今越来越多的人开始关注并参与到华为认证的学习中来。 其中&#xff0c;华为认证数据通信专家(HCIE-Datacom)作为华为认证体系中的高级认证&#xff0c;备受瞩目。 那么&#xff0c;关于HCIE-Datacom证书的有效期以及HCIE考试的内容&#xff0c;你知道多少呢&#xff1f;下…...

OpenCV中的边缘检测技术及实现

边缘检测是在电脑如何理解图片这一问题中的一环&#xff0c;它帮助电脑找出照片里的轮廓和分界线。想象一下你在看一幅黑白漫画&#xff0c;轮廓线定义了每一个角色和物体&#xff0c;而电脑要做的&#xff0c;就是通过边缘检测来找出这些线条。这在很多像是图像分析这样的领域…...

机器学习基础(一)理解机器学习的本质

导读&#xff1a;在本文中&#xff0c;将深入探索机器学习的根本原理&#xff0c;包括基本概念、分类及如何通过构建预测模型来应用这些理论。 目录 机器学习 机器学习概念 相关概念 机器学习根本&#xff1a;模型 数据的语言&#xff1a;特征与标签 训练与测试&#xf…...

Eclipse - Makefile generation

Eclipse - Makefile generation References right mouse click on the project -> Properties -> C/C Build -> Generate Makefiles automatically 默认会在 Debug 目录下创建 Makefile 文件。 References [1] Yongqiang Cheng, https://yongqiang.blog.csdn.net/...

Sora:新一代实时音视频通信框架

一、Sora简介 Sora是一个开源的实时音视频通信框架&#xff0c;旨在提供高效、稳定、可扩展的音视频通信解决方案。它基于WebRTC技术&#xff0c;支持跨平台、跨浏览器的实时音视频通信&#xff0c;并且具备低延迟、高并发、易集成等特点。 --点击进入Sora(一定要科学哦&#x…...

龟兔赛跑算法

一、题目 给定一个长度为 n1 的数组nums&#xff0c;数组中所有的数均在 1∼n1 的范围内&#xff0c;其中 n≥1。 请找出数组中任意一个重复的数。 样例 给定 nums [2, 3, 5, 4, 3, 2, 6, 7]。返回 2 或 3。 二、解析 解决这个问题的一种有效方法是使用快慢指针&#xf…...

Yii2项目使用composer异常记录

问题描述 在yii2项目中&#xff0c;使用require命令安装依赖时&#xff0c;出现如下错误提示 该提示意思是&#xff1a;composer运行时&#xff0c;执行了yiisoft/yii2-composer目录下的插件&#xff0c;但是该插件使用的API版本是1.0&#xff0c;但是当前的cmposer版本提供的…...

XCTF-web-easyupload

试了试php&#xff0c;php7&#xff0c;pht&#xff0c;phtml等&#xff0c;都没有用 尝试.user.ini 抓包修改将.user.ini修改为jpg图片 在上传一个123.jpg 用蚁剑连接&#xff0c;得到flag...

vscode(仍待补充)

写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh&#xff1f; debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...

Python爬虫实战:研究feedparser库相关技术

1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...

MVC 数据库

MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...

跨链模式:多链互操作架构与性能扩展方案

跨链模式&#xff1a;多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈&#xff1a;模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展&#xff08;H2Cross架构&#xff09;&#xff1a; 适配层&#xf…...

ServerTrust 并非唯一

NSURLAuthenticationMethodServerTrust 只是 authenticationMethod 的冰山一角 要理解 NSURLAuthenticationMethodServerTrust, 首先要明白它只是 authenticationMethod 的选项之一, 并非唯一 1 先厘清概念 点说明authenticationMethodURLAuthenticationChallenge.protectionS…...

土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等

&#x1f50d; 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术&#xff0c;可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势&#xff0c;还能有效评价重大生态工程…...

2023赣州旅游投资集团

单选题 1.“不登高山&#xff0c;不知天之高也&#xff1b;不临深溪&#xff0c;不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...

sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!

简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求&#xff0c;并检查收到的响应。它以以下模式之一…...

Fabric V2.5 通用溯源系统——增加图片上传与下载功能

fabric-trace项目在发布一年后,部署量已突破1000次,为支持更多场景,现新增支持图片信息上链,本文对图片上传、下载功能代码进行梳理,包含智能合约、后端、前端部分。 一、智能合约修改 为了增加图片信息上链溯源,需要对底层数据结构进行修改,在此对智能合约中的农产品数…...