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

【线程】POSIX信号量---基于环形队列的生产消费者模型

信号量概念

这篇文章是以前写的,里面讲了 System V的信号量的概念,POSIX信号量和SystemV信号量作用相同,都是用于同步操作,达到无冲突的访问共享资源目的。 但POSIX可以用于线程间同步。



信号量的概念

POSIX信号量的接口

初始化信号量

参数:
pshared:0表示线程间共享,非0表示进程间共享
value:信号量初始值,资源的初始值

销毁信号量 

等待信号量,P操作,表示要使用资源,将信号量值加1

发布信号量,V操作,表示资源使用完毕,可以归还资源了,将信号量值加1

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

生产消费者模型      

这篇文章里的生产者消费者模型,是基于一个queue展开了,也就是把queue当成一个整体来看,当成一个资源,我们对他进行加锁保护(锁就相当于二元信号量)。下面我们把这个生产者消费者模型改一下,是基于环形队列的,不把环形队列看成一个整体,把环形队列里的每个块看成很多个资源,这时候线程并发访问时会产生问题,就需要用信号量来解决

环形队列采用数组模拟,用模运算来模拟环状特性,生产者Prpducter向队列里生产任务,消费者Consumer向队列里拿数据

怎么判空和满?

当P,V指向同一个位置的时候,就是空或者满

怎么保证它们不访问同一个位置? 用信号量来管理

 

生产者关注的是空间资源,消费者关注的是数据资源,这两种资源可以分别用两种信号量来管理 ,假如开始的时候,空间资源是队列的大小,数据资源为0,此时生产者可以申请空间资源信号量成功,他就可以生产,但是消费者申请数据资源信号量时就失败等待,用两个信号量来管理者两个资源就可以很好的让生产者消费者同时访问同一个队列,但是不会访问到同一个位置,就可以很好的解决上面的问题

实现代码

大家可以把他们复制到VS Code下来看

main.cc

#include<iostream>
#include<ctime>
#include<unistd.h>
#include"RingQueue.hpp"
#include"task.hpp"using namespace std;
struct ThreadData
{RingQueue<Task>* rq;string threadname;
};void* Consumer(void* args)
{ThreadData* data=static_cast<ThreadData*>(args);RingQueue<Task>* rq=data->rq;string name=data->threadname;while(true){//消费任务Task t;rq->Pop(&t);//处理任务t.run();printf("消费者得到一个任务:%s,who:%s,结果:%s\n",t.GetTask().c_str(),name.c_str(),t.GetResult().c_str());//用printf输出比较好,不会错乱,cout打印有时会错乱// cout<<"消费者得到一个任务:"<<t.GetTask()<<",who: "<<name<<",结果为:"<<t.GetResult()<<endl;// sleep(1);}return nullptr;
}
void* Producter(void* args)
{ThreadData* data=static_cast<ThreadData*>(args);RingQueue<Task>* rq=data->rq;string name=data->threadname;while(true){//获取数据int data1=rand()%10+1;int data2=rand()%5;char op=opers[rand()%opers.size()];Task t(data1,data2,op);//生产任务rq->Push(t);printf("生产者生产一个任务:%s,who:%s\n",t.GetTask().c_str(),name.c_str());//用printf输出比较好,不会错乱,cout打印有时会错乱// cout<<"生产者生产一个任务:"<<t.GetTask()<<",who: "<<name<<endl;sleep(1);}return nullptr;
}int main()
{srand(time(nullptr));RingQueue<Task>* rq=new RingQueue<Task>(10);pthread_t c[5], p[3];for (int i = 0; i < 3; i++){ThreadData *td = new ThreadData();td->rq = rq;td->threadname = "Productor-" + std::to_string(i);pthread_create(p + i, nullptr, Producter, td);}for (int i = 0; i < 5; i++){ThreadData *td = new ThreadData();td->rq = rq;td->threadname = "Consumer-" + std::to_string(i);pthread_create(c + i, nullptr, Consumer, td);}for (int i = 0; i < 3; i++){pthread_join(p[i], nullptr);}for (int i = 0; i < 5; i++){pthread_join(c[i], nullptr);}return 0;
}

RingQueue.hpp

#pragma once
#include<iostream>
#include<string>
#include<vector>
#include<pthread.h>
#include <semaphore.h>using namespace std;template<class T>
class RingQueue
{
public:void P(sem_t& sem){sem_wait(&sem);}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 maxcap=5):maxcap_(maxcap),ringqueue_(maxcap),c_index_(0),p_index_(0){sem_init(&cdata_sem_,0,0);sem_init(&pspace_sem_,0,maxcap);pthread_mutex_init(&c_mutex_,nullptr);pthread_mutex_init(&p_mutex_,nullptr);}void Push(const T& in)//生产{P(pspace_sem_);//生产者关注空间资源,申请空间资源,空间资源--Lock(p_mutex_);//下标也是共享资源,需要加锁保护ringqueue_[p_index_]=in;p_index_++;p_index_%=maxcap_;//维持环形特性Unlock(p_mutex_);V(cdata_sem_);//数据资源增多了,数据资源++}void Pop(T* out)//消费{P(cdata_sem_);//消费者关注数据资源,申请数据资源,数据资源--Lock(c_mutex_);//下标也是共享资源,需要加锁保护*out=ringqueue_[c_index_];c_index_++;c_index_%=maxcap_;//维持环形特性Unlock(c_mutex_);V(pspace_sem_);//消费了数据,空间就增多了}~RingQueue(){sem_destroy(&cdata_sem_);sem_destroy(&pspace_sem_);pthread_mutex_destroy(&c_mutex_);pthread_mutex_destroy(&p_mutex_);}private:vector<T> ringqueue_;//用数组模拟环形队列int maxcap_;//环形队列的最大容量int c_index_;//消费者的下标int p_index_;//生产者的下标sem_t cdata_sem_;//消费者关注的数据资源sem_t pspace_sem_;//生产者关注的空间资源pthread_mutex_t c_mutex_;pthread_mutex_t p_mutex_;
};

Task.hpp

#pragma once
#include <iostream>
#include <string>using namespace std;
string opers="+-*/%";enum
{Divzero = 1,Modzero,Unknown
};class Task
{
public:Task(){}Task(int data1, int data2, char op) : _data1(data1), _data2(data2), _op(op), _result(0), _exitcode(0){}void run(){switch (_op){case '+':{_result = _data1+_data2;break;}case '-':{_result = _data1-_data2;break;}case '*':{_result = _data1*_data2;break;}case '/':{if (_data2 == 0) _exitcode = Divzero;else _result = _data1/_data2;break;}case '%':{if (_data2 == 0) _exitcode = Modzero;else _result = _data1%_data2;break;}default:{_exitcode=Unknown;break;}}}void operator()(){run();}string GetResult(){string r=to_string(_data1);r+=_op;r+=to_string(_data2);r+='=';r+=to_string(_result);r+='[';r+=to_string(_exitcode);r+=']';return r;}string GetTask(){string r=to_string(_data1);r+=_op;r+=to_string(_data2);r+="=?";return r;}private:int _data1;int _data2;char _op;int _result;int _exitcode;
};

 

 

相关文章:

【线程】POSIX信号量---基于环形队列的生产消费者模型

信号量概念 这篇文章是以前写的&#xff0c;里面讲了 System V的信号量的概念&#xff0c;POSIX信号量和SystemV信号量作用相同&#xff0c;都是用于同步操作&#xff0c;达到无冲突的访问共享资源目的。 但POSIX可以用于线程间同步。 信号量的概念 POSIX信号量的接口 初始化…...

Excel 设置自动换行

背景 版本&#xff1a;office 专业版 11.0 表格内输入长信息&#xff0c;发现默认状态时未自动换行的&#xff0c;找了很久设置按钮&#xff0c;遂总结成经验帖。 操作 1&#xff09;选中需设置的单元格/区域/行/列。 2&#xff09;点击【开始】下【对齐方式】中的【自动换…...

UNI-SOP使用说明

UNI-SOP提供了两个集成客户端&#xff1a;SpringBoot2.x/JAVA1.8和SpringBoot3.x/JAVA17&#xff0c;满足不同项目的集成需求。 平台接入 使用UNI-SOP之前&#xff0c;业务平台需要进行接入&#xff0c;完成校验后才能正常使用&#xff0c;先引入客户端开发SKD包。 <depen…...

记录-java web 生成并下载zip文件

java生成zip文件&#xff0c;zip文件分两种&#xff1a;一种是包含文件夹、一种是不包含文件夹 生成zip文件的方式 ZipOutputStream zipOutputStream new ZipOutputStream(response.getOutputStream());// 文件夹名称String folder "download/";ZipEntry ze new Z…...

大数据集群部署文档

大数据集群部署文档 注意&#xff1a;需配合大数据集群启动&检查文档进行部署&#xff0c;以便可以检验每一个组件是否部署成功。 文章目录 大数据集群部署文档一、部署前准备1. 确保所有机器可以访问外网2. 配置root用户ssh连接3. 解决Vmware ESXi 6.5 Ubuntu虚拟机ssh连…...

HTML中的表单(超详细)

一、表单 1.语法 <!-- action&#xff1a;提交的地方 method&#xff1a;提交的方式&#xff08;get会显示&#xff0c;post不会&#xff09; --> <form action"#" method"get"><p>名字&#xff1a;<input name"name" ty…...

初识 C 语言(一)

目录 一、 第一个 C 程序1. printf() 函数和 stdio.h 头文件2. main() 函数和 return 语句 二、类型和变量1. C 语言中的基本类型2. 变量的创建和命名规则3. 类型和变量的大小 三、printf() 函数和 scanf() 函数1. printf() 函数的使用2. 各种类型的输出格式3. scanf() 函数的使…...

LiveNVR监控流媒体Onvif/RTSP功能-支持电子放大拉框放大直播视频拉框放大录像视频流拉框放大电子放大

LiveNVR监控流媒体Onvif/RTSP功能-支持电子放大拉框放大直播视频拉框放大录像视频流拉框放大电子放大 1、视频广场2、录像回看3、RTSP/HLS/FLV/RTMP拉流Onvif流媒体服务 1、视频广场 视频广场 -》播放 &#xff0c;左键单击可以拉取矩形框&#xff0c;放大选中的范围&#xff…...

element ui中当el-dialog需要做全屏时,.fullscreen样式修改问题

element ui 饿了么UI中el-dialog样式修改问题 场景解决方法就是&#xff1a;去掉底部样式中的scoped,然后再进行页面级样式的更改即可。 场景 最近在使用element-ui时&#xff0c;使用到了弹窗组件&#xff1a; element-ui 官网链接地址&#xff1a; element-ui 官网链接地址…...

C++的明星之我是类001

文章目录 类类定义格式访问限定符类域 实例化实例化概念对象大小 this指针两道nt题目题目一题目二 C和C语言实现stack对比 类 类定义格式 新增一个关键字class&#xff0c;后加上类的名字&#xff0c;{}中为类的主体&#xff0c;类中的函数称为类的⽅法或者成员函数定义在类⾯…...

深度学习与应用:行人跟踪

**实验 深度学习与应用&#xff1a;行人跟踪 ** ------ **1、 实验目的** ------ - 了解行人跟踪模型基础处理流程 - 熟悉行人跟踪模型的基本原理 - 掌握 行人跟踪模型的参数微调训练以及推理的能力 - 掌握行人跟踪模型对实际问题的应用能力&#xff0c;了解如何在特定的场景和…...

MySQL | DATE_ADD()函数

题1&#xff1a; 现在运营想要查看用户在某天刷题后第二天还会再来刷题的平均概率。请你取出相应数据。 示例&#xff1a;question_practice_detail iddevice_idquest_idresultdate12138111wrong2021-05-0323214112wrong2021-05-0933214113wrong2021-06-1546543111right2021…...

DVWA 靶场环境搭建

作者&#xff1a;程序那点事儿 日期&#xff1a;2024/09/15 09:30 什么是DVWA: 是OWSASP官方编写的PHP网站&#xff0c;包含了各种网站常见漏洞&#xff08;漏洞靶场&#xff09;&#xff0c;可以学习攻击及修复方式。 PHP环境包含了&#xff0c;Windows/Apache/Mysql/Php g…...

Autosar学习----AUTOSAR_SWS_BSWGeneral(七)

&#x1f4a5;&#x1f4a5;&#x1f50d; &#x1f50d; 欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f421;优势&#xff1a;❤️博客内容尽量做到通俗易懂&#xff0c;逻辑清晰。 ⛳️座右铭&#xff1a;恒心&#xff0c;耐心&#xff0c;静心。 ⛳️ 欢迎一起…...

自动化测试框架集成:将Selenium集成到pytest与unittest中

目录 引言 一、Selenium简介 二、Selenium与pytest的集成 1. 安装pytest和Selenium 2. 编写测试用例 3. 运行测试 三、Selenium与unittest的集成 1. 编写测试类 2. 运行测试 四、Selenium自动化测试的最佳实践 1. 使用Page Object模式 2. 合理利用等待机制 3. 跨浏…...

华为GaussDB数据库(单机版)在ARM环境下的安装指南

一、软件版本 机器配置&#xff1a;8核16G&#xff0c;CPU: Huawei Kunpeng 920 2.9GHz操作系统&#xff1a;EulerOS 2.8 64bit with ARM数据库版本&#xff1a;GaussDB Kernel 505.1.0 build 44f4fa53 二、部署流程 2.1 新建用户 ① 以omm用户为例&#xff0c;添加一个omm用…...

计算机网络笔记002

### 课堂讨论对话 **学生A**: 老师&#xff0c;计算机网络的组成是怎样的&#xff1f;&#x1f914; **老师**: 非常好的问题&#xff01;计算机网络主要由硬件、软件和通信协议三部分组成。我们先从硬件开始讨论吧。 **学生B**: 硬件包括哪些设备呢&#xff1f;&#x1f60…...

Unity 的Event的Use()方法

对于Event的Use方法&#xff0c;其在调用后将不会再判断同类型的事件 这种情况下&#xff0c;第二个MosueDown不会进入&#xff0c;因为已经Use 如果把Use注释掉 依旧能进入第二个MosueDown 也就是说当使用了Use方法&#xff0c;相同的事件类型不会进第二遍...

数据分析师之Excel数据清洗

前言 目前&#xff0c;掌握一定的Excel技能时&#xff0c;怎么通过自己的技能实现数据分析的操作&#xff0c;就需要进行具体项目的实战&#xff0c;本身数据分析这个行业是非常吃经验的&#xff0c;既然我们是小白入坑&#xff0c;就需要多做实战演练&#xff0c;才能够实际的…...

手机解压软件加密指南:让文件更安全

在数字化时代&#xff0c;文件加密对于保护个人隐私和敏感信息的重要性不言而喻。随着互联网的飞速发展&#xff0c;我们的生活和工作越来越依赖于数字设备和网络。 然而&#xff0c;这也带来了一系列的安全风险&#xff0c;如黑客攻击、数据泄露等。文件加密技术成为了保护我…...

理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端

&#x1f31f; 什么是 MCP&#xff1f; 模型控制协议 (MCP) 是一种创新的协议&#xff0c;旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议&#xff0c;它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...

什么是库存周转?如何用进销存系统提高库存周转率?

你可能听说过这样一句话&#xff1a; “利润不是赚出来的&#xff0c;是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业&#xff0c;很多企业看着销售不错&#xff0c;账上却没钱、利润也不见了&#xff0c;一翻库存才发现&#xff1a; 一堆卖不动的旧货…...

从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)

设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile&#xff0c;新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...

vue3 定时器-定义全局方法 vue+ts

1.创建ts文件 路径&#xff1a;src/utils/timer.ts 完整代码&#xff1a; import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...

QT: `long long` 类型转换为 `QString` 2025.6.5

在 Qt 中&#xff0c;将 long long 类型转换为 QString 可以通过以下两种常用方法实现&#xff1a; 方法 1&#xff1a;使用 QString::number() 直接调用 QString 的静态方法 number()&#xff0c;将数值转换为字符串&#xff1a; long long value 1234567890123456789LL; …...

Map相关知识

数据结构 二叉树 二叉树&#xff0c;顾名思义&#xff0c;每个节点最多有两个“叉”&#xff0c;也就是两个子节点&#xff0c;分别是左子 节点和右子节点。不过&#xff0c;二叉树并不要求每个节点都有两个子节点&#xff0c;有的节点只 有左子节点&#xff0c;有的节点只有…...

Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)

参考官方文档&#xff1a;https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java&#xff08;供 Kotlin 使用&#xff09; 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...

C++使用 new 来创建动态数组

问题&#xff1a; 不能使用变量定义数组大小 原因&#xff1a; 这是因为数组在内存中是连续存储的&#xff0c;编译器需要在编译阶段就确定数组的大小&#xff0c;以便正确地分配内存空间。如果允许使用变量来定义数组的大小&#xff0c;那么编译器就无法在编译时确定数组的大…...

NXP S32K146 T-Box 携手 SD NAND(贴片式TF卡):驱动汽车智能革新的黄金组合

在汽车智能化的汹涌浪潮中&#xff0c;车辆不再仅仅是传统的交通工具&#xff0c;而是逐步演变为高度智能的移动终端。这一转变的核心支撑&#xff0c;来自于车内关键技术的深度融合与协同创新。车载远程信息处理盒&#xff08;T-Box&#xff09;方案&#xff1a;NXP S32K146 与…...

基于Java+MySQL实现(GUI)客户管理系统

客户资料管理系统的设计与实现 第一章 需求分析 1.1 需求总体介绍 本项目为了方便维护客户信息为了方便维护客户信息&#xff0c;对客户进行统一管理&#xff0c;可以把所有客户信息录入系统&#xff0c;进行维护和统计功能。可通过文件的方式保存相关录入数据&#xff0c;对…...