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

目录

一、环形队列的概念及定义

二、POSIX信号量

三、RingQueue的实现方式

3.1RingQueue.hpp的构建

3.2Thread.hpp

3.3Main.cc主函数的编写

3.4Task.hpp function包装器的使用


一、环形队列的概念及定义

此处的环形队列并不是一个真正意义上的环,而是通过对容量的取模操作来实现环状,消费者和生产者除了此队列为空或为满,其余情况下生产者和消费者都不会相遇,生产者一定会比消费者先走,因为初始时队列为空,消费者没有消费对象,所以一定是生产者进行生产。

此时环形队列遵循两个原则:

1.生产者不能将消费者套一个圈。

2.消费者不能超过生产者。

所以当consumer和productor处在同一位置时,只可能是以下两种情况:队列为空或者队列为满,

环形结构起始状态和结束状态都是一样的,不好判断为空或者为满,所以可以通过加计数器或者标记位来判断满或者空。另外也可以预留一个空的位置,作为满的状态。

二、POSIX信号量

POSIX信号量和SystemV信号量作用相同,都是用于同步操作,达到无冲突的访问共享资源目的。 但POSIX可以用于 线程间同步。  

#include <semaphore.h> 
int sem_init(sem_t *sem, int pshared, unsigned int value); 
参数: pshared:值为0表示线程间共享,非零表示进程间共享 value:信号量初始值 

我们可以将其理解为一个计数器,value是初始值,对sem做初始化。

销毁信号量

int sem_destroy(sem_t *sem); 

等待信号量

功能:等待信号量,会将信号量的值减1 
int sem_wait(sem_t *sem); //P() 

发布信号量

功能:发布信号量,表示资源使用完毕,可以归还资源了。将信号量值加1。 
int sem_post(sem_t *sem);//V() 

三、RingQueue的实现方式

3.1RingQueue.hpp的构建

将和环形队列相关的控制方法进行封装,通过模板传入Thread模板之中,之后每个线程都能看到环形队列的相关方法及规则,从而更好的对所有的线程进行管理,依旧是遵循Linux中的先描述,再组织。

#pragma once#include <iostream>
#include <string>
#include <vector>
#include <pthread.h>    
#include <semaphore.h>// 单生产,单消费
// 多生产,多消费
// "321":
// 3: 三种关系
// a: 生产和消费互斥和同步
// b: 生产者之间:
// c: 消费者之间:
// 解决方案:加锁
// 1. 需要几把锁?2把
//一把锁维护消费者和消费者之间的关系,另一把维护生产者和生产者之间的关系
//而生产者和消费者之间的关系则可以通过信号量来进行协调
template<typename T>
class RingQueue
{
private:void P(sem_t &sem){//等待信号量,将该信号量-1sem_wait(&sem);}void V(sem_t &sem){//发布信号量,将该信号量+1sem_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):_ring_queue(cap),_cap(cap),_productor_step(0),_consumer_step(0){sem_init(&_room_sem,0,_cap);//刚开始生产者可生产空间为_capsem_init(&_data_sem,0,0);pthread_mutex_init(&_productor_mutex,nullptr);pthread_mutex_init(&_consumer_mutex,nullptr);}//生产void Enqueue(const T &in){//生产行为P(_room_sem);//生产者可用空间--Lock(_productor_mutex);//一定有空间_ring_queue[_productor_step++]=in;_productor_step%=_cap;Unlock(_productor_mutex);V(_data_sem);//消费者可消费data++,去通知此时在_data_sem等待的消费者,信号量不为0就会进行唤醒}//消费void Pop(T* out){//消费行为P(_data_sem);//在竞争锁之前先申请信号量,Lock(_consumer_mutex);*out=_ring_queue[_consumer_step++];//拿到队列中的任务_consumer_step %= _cap;  Unlock(_consumer_mutex);V(_room_sem);}~RingQueue(){sem_destroy(&_room_sem);sem_destroy(&_data_sem);pthread_mutex_destroy(&_productor_mutex);pthread_mutex_destroy(&_consumer_mutex);}
private://1.构造环形队列std::vector<T> _ring_queue;int _cap;//环形队列的容量上限//2.生产消费者的下标int _productor_step;int _consumer_step;//3.定义信号量sem_t _room_sem;//生产者关心sem_t _data_sem;//消费者关心//4.定义锁 维护多生产多消费之间的互斥关系pthread_mutex_t _productor_mutex;pthread_mutex_t _consumer_mutex;
};

3.2Thread.hpp

Thread.hpp的详细实现方式已经在之前的博客中做过详细解读和解析。

#ifndef __THREAD_HPP__
#define __THREAD_HPP__#include <iostream>
#include <string>
#include <unistd.h>
#include <functional>
#include <pthread.h>namespace ThreadModule
{template<typename T>using func_t=std::function<void(T&,std::string name)>;template<typename T>class Thread{public:void Excute(){_func(_data,_threadname);}public:Thread(func_t<T> func,T&data,const std::string& name="none-name"):_func(func),_data(data),_threadname(name),_stop(true){}static void* threadroutine(void* args)//static成员函数没有this{Thread<T> *self = static_cast<Thread<T> *>(args);self->Excute();return nullptr;}bool Start(){int n=pthread_create(&_tid,nullptr,threadroutine,this);//把this传给threadroutine让其完成调用if(!n){_stop=false;return true;}else{return false;}}void Detach(){if(!_stop){pthread_detach(_tid);}}void Join(){if(!_stop){pthread_join(_tid,nullptr);}}std::string name(){return _threadname;}void Stop(){_stop = true;}~Thread(){}private:pthread_t _tid;std::string _threadname;T& _data;//要传入所执行函数的参数func_t<T> _func;//线程要执行的函数bool _stop;};
}
#endif

3.3Main.cc主函数的编写

在main函数中,使用了三层封装,更加清晰的梳理了环形队列的使用过程和对生产消费者两方的控制,以及对ringqueue.hpp和thread.hpp的调用和联动。

#include "RingQueue.hpp"
#include "Thread.hpp"
#include "Task.hpp"
#include <string>
#include <vector>
#include <unistd.h>
#include <ctime>//我们需要的是向队列中投递任务
using namespace ThreadModule;
using ringqueue_t=RingQueue<Task>;void Consumer(ringqueue_t &rq,std::string name)
{while(true){sleep(2);//1 消费任务Task t;rq.Pop(&t);//t拿到队列中的任务std::cout<<"Consumer handler task: "<<"["<<name<<"]"<<std::endl;//2处理任务t();}
}void Productor(ringqueue_t &rq,std::string name)
{//srand(time(nullptr)^pthread_self());while(true){rq.Enqueue(Download);std::cout<<"Productor: "<<"["<<name<<"]"<<std::endl;}
}void InitComm(std::vector<Thread<ringqueue_t>> *threads,int num,ringqueue_t &rq,func_t<ringqueue_t> func,const std::string &who)
{for(int i=0;i<num;i++){std::string name="thread-"+std::to_string(i+1)+"-"+who;threads->emplace_back(func,rq,name);//threads->back()->Start();//为什么不直接start://1.直接start会转到thread中去调用pthread_create}
}
void InitConsumer(std::vector<Thread<ringqueue_t>> *threads, int num, ringqueue_t &rq)
{InitComm(threads, num, rq, Consumer, "consumer");
}void InitProductor(std::vector<Thread<ringqueue_t>> *threads, int num, ringqueue_t &rq)
{InitComm(threads, num, rq, Productor, "productor");
}void WaitAllThread(std::vector<Thread<ringqueue_t>> &threads)
{for(auto &thread:threads){thread.Join();}
}
void StartAll(std::vector<Thread<ringqueue_t>> &threads)
{for(auto &thread:threads){std::cout<<"start: "<<thread.name()<<std::endl;thread.Start();}
}
int main()
{ringqueue_t *rq=new ringqueue_t(10);std::vector<Thread<ringqueue_t>> threads;InitProductor(&threads,1,*rq);InitConsumer(&threads,1,*rq);StartAll(threads);WaitAllThread(threads);return 0;
}

3.4Task.hpp function包装器的使用

Task是一个function<void()>的类型,也就是说用Task实例化出的模板可以接收任意类型的函数方法(也就是生产消费者模型中的任务)这样就最大的实现了来什么执行什么,大大提高了代码的灵活性可拓展性。

#pragma#include <iostream>
#include <functional>using Task=std::function<void()>;void Download()
{std::cout<<"this is adownload task"<<std::endl;
}

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

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

相关文章

微信小程序之横向列表展示

效果图 参考微信小程序可看 代码&#xff1a; <view class"lbtClass"><view class"swiper-container"><scroll-view class"swiper" scroll-x"true" :scroll-left"scrollLeft"><block v-for"(six…

centos7.5 安装mongo客户端

参考&#xff1a; https://doc.hcs.huawei.com/zh-cn/usermanual/dds/dds_faq_0018.html https://www.cnblogs.com/zhaoyingjie/p/17784968.html mongo 和 mongosh 的区别database - Difference in mongo --version and mongosh --version - Stack Overflow mongoDB 5.0 之后…

4.1 四个子空间的正交性

一、四个子空间的正交性 如果两个向量的点积为零&#xff0c;则两个向量正交&#xff1a; v ⋅ w v T w 0 \boldsymbol v\cdot\boldsymbol w\boldsymbol v^T\boldsymbol w0 v⋅wvTw0。本章着眼于正交子空间、正交基和正交矩阵。两个子空间的中的向量&#xff0c;一组基中的向…

Python多语言欧拉法和预测校正器实现

&#x1f4dc;流体力学电磁学运动学动力学化学和电路中欧拉法 &#x1f4dc;流体力学电磁学运动学动力学化学和电路中欧拉法示例&#xff1a;Python重力弹弓流体晃动微分方程模型和交直流电阻电容电路 ✒️多语言实现欧拉法和修正欧拉法 在数学和计算科学中&#xff0c;欧拉…

C# VTK 移动旋转

对vtk 场景中一个或多个选中物体进行移动旋转。 交互移动旋转坐标系 首先我们创建旋转的交互坐标系&#xff0c;三个移动Actor&#xff0c;三个旋转Actor&#xff0c;还需要4个定位坐标的小球Actor。 public class CoordinateActor 中添加Actor// 当前选中的Actorpublic vtkAc…

C语言---数据结构(1)--时间复杂和空间复杂度计算

1.什么是时间复杂度和空间复杂度 1.1算法效率 算法效率分为时间效率和空间效率 时间效率被称为时间复杂度&#xff0c;而空间效率被称作空间复杂度。 时间复杂度主要衡量的是一个算法的运行速度&#xff0c;而空间复杂度主要衡量一个算法所需要的额外空间&#xff0c;在计算…

机器人阻抗控制相关文献学习(阻抗实现)

机器人阻抗是一个描述机器人与环境交互时动态特性的概念。 定义&#xff1a; 阻抗在机器人领域中&#xff0c;通常用来描述机器人与其环境之间的相互作用。当机器人与环境接触时&#xff0c;环境对机器人施加一个作用力&#xff0c;而机器人也会对环境施加一个反作用力。这个反…

PMP与软考的区别? 我该学习哪个?

PMP&#xff08;项目管理专业人士&#xff09;和软考&#xff08;软件工程师考试&#xff09;是两种不同领域的认证考试&#xff0c;分别专注于项目管理和软件工程师领域。下面将对它们做详细介绍。 一、PMP PMP作为项目管理领域的国际认证考试&#xff0c;由美国项目管理协会…

WordPress CDN是什么?CDN有什么作用?

您想让您的网站加载速度更快吗&#xff1f; 网站所有者希望网站加载速度快&#xff0c;内容丰富&#xff0c;功能强大&#xff0c;吸引用户。然而&#xff0c;添加这些功能可能会降低网站速度&#xff0c;难以快速向全球用户提供内容。 这就是为什么许多WordPress网站使用 CDN…

小型数据中心是什么?如何建设?

在数字化时代&#xff0c;小型数据中心正成为许多企业和组织加强数据管理和服务扩展的理想选择。与传统大型数据中心相比&#xff0c;小型数据中心以其灵活性、高效性和相对较低的运营成本吸引着越来越多的关注。然而&#xff0c;要成功建设一个小型数据中心&#xff0c;并确保…

Web网页端IM产品RainbowChat-Web的v7.0版已发布

一、关于RainbowChat-Web RainbowChat-Web是一套Web网页端IM系统&#xff0c;是RainbowChat的姊妹系统&#xff08;RainbowChat是一套基于开源IM聊天框架 MobileIMSDK (Github地址) 的产品级移动端IM系统&#xff09;。 ► 详细介绍&#xff1a;http://www.52im.net/thread-2…

特氟龙FEP离心管50ml30ml圆底赛默飞耐酸碱Thermo3114-0050离心管

FEP离心管&#xff1a;又叫聚全氟乙丙烯离心管&#xff0c;特氟龙离心管&#xff0c;F46离心管等。 其主要特性有&#xff1a; 1、可耐高温205℃、耐腐蚀性、不吸附性、透明可见、方便实验操作&#xff1b; 2、可适配于国内外各厂家离心机使用。 3、内壁光滑&#xff0c;不…

解决安全规模问题:MinIO 企业对象存储密钥管理服务器

在强大可靠的存储解决方案领域&#xff0c;MinIO 作为持久层脱颖而出&#xff0c;为组织提供安全、持久和可扩展的存储选项。MinIO 通常负责处理关键任务数据&#xff0c;在确保高可用性方面发挥着至关重要的作用&#xff0c;有时甚至在全球范围内。存储数据的性质&#xff0c;…

电路学习——经典运放电路(2024.06.21)

参考链接1: 11个经典运放电路 在此感谢各位前辈大佬的总结&#xff0c;写这个只是为了记录学习大佬资料的过程&#xff0c;内容基本都是搬运的大佬博客&#xff0c;觉着有用自己搞过来自己记一下&#xff0c;如果有大佬觉着我搬过来不好&#xff0c;联系我删。 电路学习——经典…

振弦式渗压计:土壤力学与地下水流动研究的关键工具

当谈论到地下水流动和土壤力学时&#xff0c;振弦式渗压计是一种至关重要的工具。这篇文章将探讨振弦式渗压计的原理、工作方式以及其在土壤力学和地下水流动研究中的重要性。 振弦式渗压计的原理 振弦式渗压计利用了振动传感器和压力传感器的组合来测量土壤中的水压力。其基本…

车联网全方位安全适配与领先架构

设想一下如下场景&#xff1a; 您钟爱的座驾&#xff0c;在毫无外力破坏迹象的情况下&#xff0c;突然被侵入&#xff0c;远程启动&#xff0c;然后绝尘而去… 别以为这只是大银幕上的虚构桥段&#xff0c;事实上&#xff0c;这一幕在现实中已经上演。 某款备受欢迎的车型&a…

职场新宠:ONLYOFFICE——办公协作的得力助手

&#x1f3a0;前言 在快节奏的职场环境中&#xff0c;高效、便捷的办公软件成为每一位职场人士不可或缺的工作伙伴。当我们谈论职场办公软件时&#xff0c;许多人首先会想到Microsoft Office、wps等老牌软件。 然而&#xff0c;有一款宝藏的办公软件ONLYOFFICE&#xff0c;凭…

ESP32 esp-idf esp-adf环境安装及.a库创建与编译

简介 ESP32 功能丰富的 Wi-Fi & 蓝牙 MCU, 适用于多样的物联网应用。使用freertos操作系统。 ESP-IDF 官方物联网开发框架。 ESP-ADF 官方音频开发框架。 文档参照 https://espressif-docs.readthedocs-hosted.com/projects/esp-adf/zh-cn/latest/get-started/index.…

Postgres 多实例实例部署方式(Windows)

复制之前数据库中的data文件 1、进入"服务"&#xff0c;停止服务 2、直接复制data整个文件夹到另一个路径&#xff0c;打开"postgresql.conf"文件夹&#xff0c;修改port为其他端口 启动新的服务实例 1、cmd输入命名启动服务 pg_ctl -D "D:\PG\N…