POSIX信号量

article/2025/7/5 20:56:20

目录

初始化信号量

销毁信号量

 等待信号量

发布信号量

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

代码实现


POSIX信号量和SystemV信号量作用相同,都是用于同步操作,达到无冲突的访问共享资源目的。本质就是一个计数器,是对特定资源的预定机制,本身相比于基本类型,POSIX信号是原子的,POSIX可以用于线程间同步,信号量把对临界资源是否存在,就绪等的条件,以原子性的形式呈现在临界资源前就判断了。

初始化信号量
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
参数:pshared:0表⽰线程间共享,⾮零表⽰进程间共享value:信号量初始值
销毁信号量
int sem_destroy(sem_t *sem);
 等待信号量
功能:等待信号量,会将信号量的值减1
int sem_wait(sem_t *sem); //P()
//申请成功执行后面的代码,申请失败阻塞挂起
发布信号量
功能:发布信号量,表⽰资源使⽤完毕,可以归还资源了。将信号量值加1。
int sem_post(sem_t *sem);//V()

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


环形队列采用数组模拟,用模运算来模拟环状特性

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

设计思路

  • 约定一:空,生产者先行
  • 约定二:满,消费者先行
  • 约定三:生产者不能快过消费者,不能套一个圈以上,防止生产满了还生产
  • 约定四:消费者不能超过生产者,防止没有生产好就消费

总结:只有不是同一个位置就能并发进行,用数组来模拟环形队列,开始没有东西,生产者先生产,消费者等待,等生产者生产了数据,唤醒消费者生产,此时开始并发,假如生产者快过消费者,只有判断下一个生产节点是否和消费者位置一致就进行等待,等消费者消费完唤醒,假如消费者快过生产者,只有消费者下一个节点是生产者处的位置时,此时没有数据就进入等待队列,等待唤醒,数据为空为满都是互斥

但是我们现在有信号量这个计数器,就很简单的进行多线程间的同步过程。

代码实现

单生产,单消费

Sem.hpp

#include <iostream>
#include <pthread.h>
#include <semaphore.h>const int defaultvalue = 5;
namespace SemModule
{class Sem{public:Sem(unsigned int sem_value = defaultvalue){sem_init(&_sem, 0, sem_value);}~Sem() {sem_destroy(&_sem);}void V(){int n=sem_post(&_sem);}void P(){int n=sem_wait(&_sem);}private:sem_t _sem;};
}

Main.cc 

#include<iostream>
#include <iostream>
#include <pthread.h>
#include<unistd.h>
#include"RingQueue.hpp"
//消费void *consumer(void *args)
{RingQueue<int> *rq = static_cast<RingQueue<int>*>(args);while(true){sleep(2);int t =0;rq->Pop(&t);std::cout<<"消费拿到一个数据:"<<t<<std::endl;}
}
//生产
void *productor(void *args)
{   int data=1;RingQueue<int> *rq = static_cast<RingQueue<int>*>(args);while(true){   std::cout<<"生产了一个任务"<<std::endl;rq->Equeue(data++);}
}int main()
{// 申请阻塞队列RingQueue<int> *rq = new RingQueue<int>();// 创建消费则模型pthread_t c[1], p[1];pthread_create(c, nullptr, consumer, rq);pthread_create(p, nullptr, productor, rq);pthread_join(c[0],nullptr);pthread_join(p[1],nullptr);return 0;
}

RingQueue.hpp

#include <vector>
#include <iostream>
#include "Sem.hpp"using namespace SemModule;
static const int gcap = 5;template <typename T>
class RingQueue
{
public:RingQueue(int cap = gcap): _rq(cap), _cap(cap), _blank_sem(cap), _p_step(0), _data_sem(0), _c_step(0){}~RingQueue() {}// 生产者调用void Equeue(const T &in){// 1.申请信号量,空位置信号量_blank_sem.P();//2.生产_rq[_p_step]=in;//3.更新下标,并维持环形特性_p_step++;_p_step%=_cap;//4.通知_data_sem.V();}// 消费者调用void Pop(T *out){//1.申请信号量,数据信号量_data_sem.P();//2.消费*out=_rq[_c_step];//3.更新下标,并维持环形特性_c_step++;_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;         //消费者下标位置
};

多生产,多消费

Main.cc

#include<iostream>
#include <iostream>
#include <pthread.h>
#include<unistd.h>
#include"RingQueue.hpp"
//消费
struct threaddata
{
RingQueue<int>*rq;
std::string name;
};void *consumer(void *args)
{threaddata *td=static_cast<threaddata*>(args);while(true){sleep(2);int t =0;td->rq->Pop(&t);std::cout<<td->name<<"消费拿到一个数据:"<<t<<std::endl;}
}
//生产
void *productor(void *args)
{   threaddata *td=static_cast<threaddata*>(args);int data=1;while(true){   sleep(1);std::cout<<td->name<<"生产了一个任务"<<data<<std::endl;td->rq->Equeue(data++);}
}int main()
{ RingQueue<int> *rq = new RingQueue<int>();// 创建消费则模型pthread_t c[2], p[3];threaddata *td1=new threaddata();td1->name="cthread-1";td1->rq=rq;pthread_create(c, nullptr, consumer, td1);threaddata *td2=new threaddata();td2->name="cthread-2";td2->rq=rq;pthread_create(c+1, nullptr, consumer, td2);threaddata *td3=new threaddata();td3->name="pthread-3";td3->rq=rq;pthread_create(p, nullptr, productor, td3);threaddata *td4=new threaddata();td4->name="pthread-4";td4->rq=rq;pthread_create(p+1, nullptr, productor, td4);threaddata *td5=new threaddata();td5->name="pthread-5";td5->rq=rq;pthread_create(p+2, nullptr, productor, td5);pthread_join(c[0],nullptr);pthread_join(c[1],nullptr);pthread_join(p[0],nullptr);pthread_join(p[1],nullptr);pthread_join(p[2],nullptr);return 0;
}

 RInfQueue.hpp

#include <vector>
#include <iostream>
#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), _p_step(0), _data_sem(0), _c_step(0){}~RingQueue() {}// 生产者调用void Equeue(const T &in){// 1.申请信号量,空位置信号量_blank_sem.P(); // 先申请信号量在申请锁效率高一点,先把信号量顺序依次分好,然后分到的再去申请锁,在申请锁的同时别的线程也可以去申请信号量,如果先// 申请锁在申请信号量就慢了{LockGuard lockguard(_pmutex);// 2.生产_rq[_p_step] = in;// 3.更新下标,并维持环形特性_p_step++;_p_step %= _cap;}// 4.通知_data_sem.V();}// 消费者调用void Pop(T *out){// 1.申请信号量,数据信号量_data_sem.P();{LockGuard lockguard(_cmutex);// 2.消费*out = _rq[_c_step];// 3.更新下标,并维持环形特性_c_step++;_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;
};

 Mutex.cc

#include <pthread.h>
#include <iostream>
namespace MutexModule
{class Mutex{public:Mutex(){pthread_mutex_init(&_mutex, nullptr);}void Lock(){int n = pthread_mutex_lock(&_mutex);(void)n;}void Unlock(){int n = pthread_mutex_unlock(&_mutex);(void)n;}~Mutex(){pthread_mutex_destroy(&_mutex);}pthread_mutex_t *get(){return &_mutex;}private:pthread_mutex_t _mutex;};class LockGuard{public:LockGuard(Mutex &mutex):_mutex(mutex){_mutex.Lock();}~LockGuard(){_mutex.Unlock();}private:Mutex &_mutex;};
}

Sem.cpp

#include <iostream>
#include <pthread.h>
#include <semaphore.h>const int defaultvalue = 5;
namespace SemModule
{class Sem{public:Sem(unsigned int sem_value = defaultvalue){sem_init(&_sem, 0, sem_value);}~Sem() {sem_destroy(&_sem);}void V(){int n=sem_post(&_sem);}void P(){int n=sem_wait(&_sem);}private:sem_t _sem;};
}


http://www.hkcw.cn/article/lsTfOLNGcq.shtml

相关文章

P23:实现天气预测

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 一、知识介绍 1. 数据加载与预处理 知识点&#xff1a; pd.read_csv()&#xff1a;Pandas读取CSV文件的核心方法pd.to_datetime()&#xff1a;将字符串转换…

BERT模型原理与Fine-tuning实战指南

BERT模型原理与Fine-tuning实战指南 系统化学习人工智能网站&#xff08;收藏&#xff09;&#xff1a;https://www.captainbed.cn/flu 文章目录 BERT模型原理与Fine-tuning实战指南摘要引言1. BERT核心原理解析1.1 Transformer架构基础1.2 预训练任务设计1.3 模型变体对比 2…

谷歌地图15周年焕新:界面全面升级 功能重磅加码

谷歌卫星高清地图 下载链接&#xff1a;https://pan.quark.cn/s/c6069864c9f3 Google Earth Pro-7.3.6.9796-x64 Google Earth WinMac安装版 GoogleEarthProPortable googleearthpromac-intel-7.3.6.10155 GoogleEarthProWin-7.3.6.10155 GoogleEarthProWin-x64-7.3.6.10…

ONNX模型的动态和静态量化

引言  通常我们将模型转换为onnx格式之后&#xff0c;模型的体积可能比较大&#xff0c;这样在某些场景下就无法适用。最近想在移动端部署语音识别、合成模型&#xff0c;但是目前的效果较好的模型动辄几个G&#xff0c;于是便想着将模型压缩一下。本文探索了两种压缩方法&…

怎样在视频号卖货入口是哪里?需要注意哪些?

今天给大家分享下我们团队降低违规的一些心法和落地技巧以及视频号卖货的入口希望对大家有所帮助。 一、视频号卖货入口在哪里&#xff1f; 1、点击【视频号】右上角人像&#xff0c;进入【创作中心】下方点击【带货中心】&#xff0c;进入视频号带货中心。 初次进入带货中心…

Ubuntu下实现nginx反向代理

1. 多个ngx实例安装 脚本已经在deepseek的指导下完成啦&#xff01; deepseek写的脚本支持ubuntu/centos两种系统。 ins_prefix"/usr/local/" makefile_gen() {ngx$1 ngx_log_dir"/var/log/"$ngx"/"ngx_temp_path"/var/temp/"${ngx}…

2025最新Nginx安装配置保姆级教程(Windows)

下载 进入官网nginx: download 选择最新的主线版本下载 启动 解压文件夹到你想要的路径下 打开文件夹双击nginx.exe 点允许访问 然后打开浏览器输入localhost回车&#xff0c;如果出现如下页面则安装成功&#xff01; winr打开控制台进入nginx安装目录&#xff0c;输入ngi…

FreeRTOS通俗理解指南:基础概念 + 架构+ 内核组件+练手实验

RTOS 基础概念 想象一下&#xff0c;你是一个忙碌的厨师&#xff0c;在厨房里同时要完成煎牛排和煮意大利面两项任务。 1.传统单线程模式&#xff08;没有RTOS&#xff09; 如果你只能按顺序一项一项地做&#xff0c;就会是这样的过程&#xff1a; 先煎一会儿牛排然后去看看…

端午最全攻略!景点、天气、交通三件套

端午假期去哪玩?各地文旅放大招如何既能体验传统文化又能玩得轻松不踩坑?这份攻略请收好!端午假期去哪玩?端午假期各地文旅放大招端午主题活动丰富多彩部分景区还推出节日优惠政策北京北京推出1700余场活动,与广大市民游客共享初夏好时节。1700余场活动具体戳详情>>…

【DAY34】GPU训练及类的call方法

内容来自浙大疏锦行python打卡训练营 浙大疏锦行 知识点&#xff1a; CPU性能的查看&#xff1a;看架构代际、核心数、线程数GPU性能的查看&#xff1a;看显存、看级别、看架构代际GPU训练的方法&#xff1a;数据和模型移动到GPU device上类的call方法&#xff1a;为什么定义前…

干泵,干式螺杆真空泵

干式真空泵&#xff1a; 无油干式机械真空泵&#xff08;又简称干式机械泵&#xff09;是指泵能从大气压力下开始抽气&#xff0c;又能将被抽气体直接排到大气中去&#xff0c;泵腔内无油或其他工作介质&#xff0c;而且泵的极限压力与油封式真空泵同等量级或者接近的机械真空泵…

day 26 函数专题

一、函数的基本写法 def function_name(parameter1, parameter2, ...):"""Docstring: 描述函数的功能、参数和返回值 (可选但强烈推荐)"""# 函数体: 实现功能的代码# ...return value # 可选&#xff0c;用于返回结果def 关键字&#xff1a;用于…

目标检测学习

RCNN 默认找出2000个可能存在目标的候选区域 region proposal将候选区域调整为适合AlexNet网络的输入图像的大小&#xff0c;通过CNN对候选区域提取特征向量&#xff0c;2000个建议区域经过Alexnet生成20004096的特征矩阵将20004096的特征向量经过20个类别的svm分类器进行分类&…

谷歌Gemma模型实现智能看病、翻译手语、与海豚沟通

目录 引言&#xff1a;AI的新篇章——从通用走向专属的智慧革命 一、 MedGemma&#xff1a;智能医疗的守护者与革新者 1.1 MedGemma的双重火力 1.2 高效部署与开发者生态 1.3 未来展望 二、SignGemma&#xff1a;跨越无声世界的沟通桥梁 2.1 SignGemma的核心能力 2.2 从…

TDengine 运维——巡检工具(定期检查)

背景 TDengine 在运行一段时间后需要针对运行环境和 TDengine 本身的运行状态进行定期巡检&#xff0c;本文档旨在说明如何使用巡检工具对 TDengine 的运行环境进行自动化检查。 安装工具使用方法 工具支持通过 help 参数查看支持的语法 Usage: taosinspect [OPTIONS]Check…

火语言UI组件--地图

【组件功能】&#xff1a;调用高德地图api,可通过定义经纬度定位显示具体位置。 样式预览 设置 基础设置 属性名称属性释义输入值类型开发者Key(key)高德地图 JSAPI 开发者Key字符串类型安全密钥(securityJsCode)高德地图 JSAPI 安全密钥字符串类型缩放级别(zoom)设置地图缩…

安防通信枢纽新贵:HT-CKNU 网络报警服务器深度解析

在安防体系不断升级的当下&#xff0c;报警服务器作为连接前端设备与接警中心的关键枢纽&#xff0c;其性能优劣直接关乎安全防护的成效。HT-CKNU 网络报警服务器凭借卓越的技术架构、广泛的兼容性和便捷的操作体验&#xff0c;正逐渐成为安防行业的中流砥柱&#xff0c;为各领…

打通仿真数据孤岛,实现精细化权限管理,「共享空间」深度解析

在上一期《资深仿真工程师必备&#xff01;详解平台“控制中枢”——「命令终端」》中&#xff0c;我们详解了如何通过命令行实现作业高效提交与资源调度。然而&#xff0c;工业仿真从来不是单兵作战——复杂模型的多方协作、海量数据的跨团队流转&#xff0c;才是研发场景的常…

桌面工具站

桌面工具站软件&#xff0c;可以将桌面软件、word文档和网页等拖拽到工具站&#xff0c;在工具站点击直接跳转&#xff0c;方便快捷。 可以拖拽进入软件&#xff0c;也可以迪纳基添加&#xff0c;添加界面如下&#xff1a; 支持自定义排序 可自定义背景&#xff0c;效果如下; 支…

人工智能编程学习心得:从零基础到独立开发的蜕变之路

引言&#xff1a;一场改变认知的技术之旅 2022年冬天&#xff0c;我在深夜的办公室里经历了第一次深度学习模型的完整训练过程。当GPU使用率曲线从波动到平稳&#xff0c;当验证集准确率突破85%的那一刻&#xff0c;显示器的蓝光映照着满桌的咖啡杯&#xff0c;这个场景成为我…