发布时间:2026/9/3 8:27:37
Linux:信号量,生产者消费者模型和日志实现 目录1.什么是信号量2.基于环形队列的生产者消费者模型3.日志的实现1.什么是信号量信号量的本质是一把计数器描述资源的数量。申请信号量的本质是用来预定资源的比如买电影票。因为信号量也是共享资源同时只能一个线程访问所以访问信号量直接设计成了原子操作。信号量的本质是一把计数器。申请信号本质就是预定资源PV操作是原子的多个线程可以同时访问临界资源的条件如果公共资源当作整体多个线程不访问临界资源的同一个位置1.申请信号量2.访问指定的一个位置3.释放信号量这样就可以多个进程并发地访问这份资源。只要申请进来之后就不用再判断有没有了因为通过信号量已经确定了肯定可以分配给你。信号量可以定义全局和局部的sem 就是要传入的信号量。value 就是有多少资源位置#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()2.基于环形队列的生产者消费者模型上一篇生产者与消费者模型是基于queue的整体使用是安全的其空间可以动态分配这里基于固定大小的环形队列重写这个程序有两点需要注意生产者不能把消费者套一个圈消费者不能超过生产者生产者与消费者只有两个场景会指向同一个位置需要同步为空为满。其他情况他们根本就不会指向同一个位置可以同时执行不用加锁。空时只能让生产者生产满时只能让消费之消费局部需要维持互斥与同步。不同角色对资源的认识生产者需要空间 sem_space 消费者需要数据 sem_data 。假设有N个位置开始时 sem_space N sem_data 0生产者伪代码 P(sem_space) //生产 V(sem_data) 消费者伪代码: P(sem_data) //消费 V(sem_space)两者当发现没有资源时都会被阻塞。单生产单消费RingQueue代码#pragma once #include iostream #include vector #include semaphore.h const int defaultsize 5; templateclass T class RingQueue { void P(sem_t sem) { sem_wait(sem);//-- 信号量是临界资源这里操作是原子的 } void V(sem_t sem) { sem_post(sem);// } public: RingQueue(int size defaultsize):_ringqueue(size),_size(size) { //定义信号量 sem_init(_space_sem,0,_size);//有_size个空间 sem_init(_data_sem,0,0);//最开始没有数据只有空间 } void Push(const T in) { //生产 P(_space_sem);//申请到说明一定有空间 _ringqueue[_p_step] in; _p_step; _p_step % _size; V(_data_sem); } void Pop(T* out) { P(_data_sem);//申请到说明一定有数据 *out _ringqueue[_c_step]; _c_step; _c_step % _size; V(_space_sem); } ~RingQueue() { sem_destroy(_space_sem); sem_destroy(_data_sem); } private: std::vectorT _ringqueue; int _size; int _p_step;//生产者的脚步 int _c_step;//消费者的脚步 sem_t _space_sem;//生产者关心的信号量因为生产和消费有时可以并发进行所有要两个。 sem_t _data_sem;//消费者关心的信号量 };用到了两个信号量_space_sem_data_sem生产者申请等待 _space_sem消费者申请等待 _data_sem因为信号量是公共资源要互斥访问所以申请获得信号量这些操作设计是原子的释放也是原子的。获得信号量之后说明一定可以对资源进行操作。V的两个作用是“释放资源 若有等待者则唤醒它”所以单生产单消费会看到生产者生产后消费者会跟着执行的现象。与阻塞队列的不同阻塞队列是当作一个整体来使用的所以需要整体加锁这里是通过信号量局部性地访问。多生产多消费中这里生产者与消费者的互斥与同步(空满通过信号量)已经实现了但是生产者与生产者之间消费者与消费者之间的互斥并没有实现因为比如多个生产者得到信号量进入到队列内部但是内部的_p_step 是共享的所以我们需要加锁防止生产完没有 _p_step导致数据被覆盖。信号量通过“阻塞/唤醒”机制没有加锁强制了生产者和消费者在缓冲区空/满时进行“同步交接”。这种机制比加锁更轻量因为加锁通常是“主动忙等”或“上下文切换”而信号量直接管理了资源计数。当队列不为空满时如果只定义一把锁生产和消费就不可能一起跑了 所以这里定义两把锁。在环形队列中多生产者多消费者先申请信号量还是先申请锁都是可以的但是先申请信号量更快先申请锁再申请信号量当多个线程到来时他们都会在锁外等待有锁时才会再申请信号量一个锁一个信号量地来。多个线程先申请信号量等待锁时多个线程可以申请信号量之后就只需要等待锁然后访问资源就行。多生产多消费RingQueue:#pragma once #include iostream #include vector #include semaphore.h #include pthread.h const int defaultsize 5; templateclass T class RingQueue { void P(sem_t sem) { sem_wait(sem);//-- 信号量是临界资源这里操作是原子的 } void V(sem_t sem) { sem_post(sem);// } public: RingQueue(int size defaultsize):_ringqueue(size),_size(size) { //定义信号量 sem_init(_space_sem,0,_size);//有_size个空间 sem_init(_data_sem,0,0);//最开始没有数据只有空间 pthread_mutex_init(_p_mutex,nullptr); pthread_mutex_init(_c_mutex,nullptr); } void Push(const T in) { //生产 P(_space_sem);//申请到说明一定有空间 pthread_mutex_lock(_p_mutex);//写在里面效率高 _ringqueue[_p_step] in; _p_step; _p_step % _size; pthread_mutex_unlock(_p_mutex); V(_data_sem); } void Pop(T* out) { P(_data_sem);//申请到说明一定有数据 pthread_mutex_lock(_c_mutex); *out _ringqueue[_c_step]; _c_step; _c_step % _size; pthread_mutex_unlock(_c_mutex); V(_space_sem); } ~RingQueue() { sem_destroy(_space_sem); sem_destroy(_data_sem); pthread_mutex_destroy(_p_mutex); pthread_mutex_destroy(_c_mutex); } private: std::vectorT _ringqueue; int _size; int _p_step;//生产者的脚步 int _c_step;//消费者的脚步 sem_t _space_sem;//生产者关心的信号量因为生产和消费有时可以并发进行所有要两个。 sem_t _data_sem;//消费者关心的信号量 pthread_mutex_t _p_mutex;//生产者的锁 pthread_mutex_t _c_mutex;//消费者的锁 };3.日志的实现可变参数列表可以设计在函数参数中的但不能只有...前面要有一个合法参数与函数调用压栈有关。我们要使用 va_list args就是char*; 保存可变参数列表va_start(args前面的参数)原理是压栈时参数在一起排列。然后 va_arg(args类型) 就可以得到传入的该类型参数类型就是如 printf(%d,%s, , )printf 会在前面字符串提取va_end(args)指向nullptr清理参数。可变参数列表使用示例计算可变参数列表传入数的和第一个参数是数据的个数#include stdio.h #include stdarg.h int sum(int count, ...) { va_list args; int sum 0; va_start(args,count); for(int i 0;i count ;i) { int value va_arg(args,int); sum value; } va_end(args); return sum; } int main() { int result sum(3,1,3,5); printf(%d\n,result);//9 return 0; }关于日志实现时间通过 time 我们可以获取当前的时间戳time_t 类型也就是uint64_t。#include time.h struct tm* localtime(const time_t *timep);localtime 会返回一个结构体有些时间是从0开始的内容如下图所示一个简单的日志代码#include iostream #include fstream #include stdarg.h #include cstdarg #include unistd.h #include sys/types.h #include sys/stat.h #include sys/fcntl.h enum{ Debug 0, Info, Warning, Error, Fatal, }; //打印到哪里 enum { Screen 10, OneFile, ClassFile }; std::string LevelToString(int level) { switch(level) { case Debug: return Debug; case Info: return Info; case Warning: return Warning; case Error: return Error; case Fatal: return Fatal; default: return Unknow; } } const int defultstyle Screen; const std::string default_filename testlog.; const std::string logdir log; class Log { public: Log():style(defultstyle),filename(default_filename) { mkdir(logdir.c_str(),0755);//存放日志的目录 }; void Enable(int sty)//打印的方式 { style sty; } std::string TimeStampExLocalTime()//获取当前时间 { time_t currtime time(nullptr); struct tm* curr localtime(currtime); char time_buffer[128]; //格式化输入到 time_buffer snprintf(time_buffer, sizeof(time_buffer), %d-%d-%d %d:%d:%d,\ curr-tm_year1900,curr-tm_mon1,curr-tm_mday,curr-tm_hour,curr-tm_min,curr-tm_sec); return time_buffer; } void WriteLogToOneFile(const std::string logname,const std::string message) { //c方式 umask(0); int fd open(logname.c_str(),O_CREAT|O_WRONLY|O_APPEND,0666); if(fd 0) return; write(fd, message.c_str(),message.size()); close(fd); //c方式 // std::ofstream out(logname,std::ofstream::app); // if(!out.is_open()) // return; // out.write(message.c_str(),message.size()); // //outmessage; // out.close(); } void WriteLogToClassFile(const std::string levelstr,const std::stringmessage) { std::string logname logdir;//往指定路径下打印 logname /; logname filename; logname levelstr;//通过文件后缀区分文件 WriteLogToOneFile(logname,message); } void WriteLog(const std::string levelstr,const std::string message) { switch(style) { case Screen: std::cout message; break; case OneFile: WriteLogToClassFile(all,message); break; case ClassFile: WriteLogToClassFile(levelstr,message); break; default: break; } } //第一个参数表示严重程度 void LogMessage(int level,const char* format , ...) { //错误的信息 char rightbuffer[1024]; va_list args; va_start(args,format);//取的起始指针args读取后面具体参数列表 //不想用 va_arg(args)一个个看我们这里直接处理 vsnprintf(rightbuffer,sizeof(rightbuffer),format,args);//format是%s,%f,%f,args就是具体对应的值 va_end(args); //记录发生的时间等等 char leftbuffer[1024]; std::string levelstr LevelToString(level); std::string currtime TimeStampExLocalTime(); std::string idstr std::to_string(getpid()); snprintf(leftbuffer, sizeof(leftbuffer), [%s][%s][%s],\ levelstr.c_str(),currtime.c_str(),idstr.c_str()); std::string loginfo leftbuffer; loginfo rightbuffer; WriteLog(levelstr,loginfo); //printf(%s%s,leftbuffer,rightbuffer); } ~Log(){}; private: int style; std::string filename; };测试示例Main.cc也可以封装一下Log lg; class Conf//配置中心 { public: Conf(int style defultstyle) { lg.Enable(style); } ~Conf() {} }; Conf conf;本篇结束

相关新闻

2026/9/3 8:27:37

WPF附加属性进阶:从布局标记到行为注入的扩展机制详解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/3 8:27:37

游戏MOD本地化部署指南:从环境准备到功能验证全流程

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/3 8:27:37

dialog封装

通用封装弹窗 / 自定义组件模板&#xff0c;可套用 // 子组件 <template><el-dialog v-model"innerFlag" title"弹窗">// 业务内容 <template #footer><span class""><el-button click"state.peopleDialogShow …

2026/9/3 8:47:43

Abaqus拓扑优化Python脚本工程实践指南

简介&#xff1a;本资源面向具备Abaqus基础操作与Python编程能力的结构仿真工程师及高年级本科生&#xff0c;聚焦于利用Abaqus CAE内嵌Python API实现自动化拓扑优化设计&#xff0c;解决传统手动建模中迭代效率低、参数调整繁琐、SIMP方法实施门槛高等实际问题。压缩包共5个文…

2026/9/3 8:47:43

STM32F103工业级交流电参量同步采样系统设计

简介&#xff1a;本资源是一套基于STM32F103单片机的智能电表核心功能实现方案&#xff0c;面向嵌入式初学者、电子类课程设计学生及电力计量方向实践开发者&#xff0c;聚焦交流电压与电流的高精度采集、实时计算与本地显示等关键问题。压缩包共155个文件&#xff0c;含36个头…

2026/9/3 8:42:42

基于STM32与Proteus的超声波测距测温仿真系统设计与实现

简介&#xff1a;本资源是一套面向嵌入式初学者与STM32进阶学习者的完整仿真项目实践包&#xff0c;聚焦超声波测距与数字温度测量双功能融合开发&#xff0c;解决传感器驱动、多外设协同及LCD实时显示等典型嵌入式系统工程问题。资源共244个文件&#xff0c;涵盖65个C语言源码…

2026/9/1 16:02:17

vSound小提琴数字处理器实操指南:从接线到演出的完整配置

电小提琴或者原声小提琴插电演出&#xff0c;第一个绕不开的坎就是声音难听。原声琴的共鸣和空气感一旦进了拾音器&#xff0c;出来的往往是一坨干瘪、发尖、带着奇怪塑料味的信号。我当初第一次把琴接上乐队调音台&#xff0c;直接被主唱吐槽"你这声音像在锯钢丝"。…

2026/9/2 9:00:32

传感器接口IC如何攻克生物化学传感的微弱信号难题?

1. 从电极到比特流&#xff1a;为什么生物化学传感必须依赖专用接口IC 做生物化学传感的人都有过类似的经历&#xff1a;明明传感器本身性能很好&#xff0c;信号输出却一塌糊涂——噪声大、漂移明显、重复性差&#xff0c;怎么调都达不到预期。很多时候问题并不在传感器&#…

2026/9/2 8:41:06

STM32F411CEU6多通道ADC采集:扫描模式+DMA实现详解

1. 多通道 ADC 的用武之地把“Multichannel ADC”和“STM32F411CEU6”这两个关键字放在一起&#xff0c;其实就是嵌入式开发里最常遇到的一类需求&#xff1a;用一块不算贵的 MCU&#xff0c;同时采集多路模拟信号。STM32F411CEU6 是 48 引脚的 Cortex-M4F 主控&#xff0c;主频…

2026/9/3 0:02:06

零基础装 OpenClaw 小龙虾 AI:Windows 一键部署教程与避坑要点

Windows 部署 OpenClaw 完整教程&#xff5c;本地 AI 智能体 5 分钟落地&#xff0c;环境配置一次搞定 版本说明&#xff1a;Windows 3.1.0 / Mac 2.7.9 写在前面 近两年开源 AI 领域有一款被称作「数字员工」的工具持续走热&#xff0c;它就是 OpenClaw&#xff0c;圈内人更习…

2026/9/3 0:02:06

Hermes Agent 本地部署新方案:Windows 整合包减少依赖报错

Windows 本地部署 Hermes 太麻烦&#xff1f;这版一键包 5 分钟快速跑通 很多人想体验 Hermes Agent&#xff0c;但真正开始部署时&#xff0c;往往会卡在环境配置这一步。 需要安装各类依赖、调试运行环境、处理路径问题&#xff0c;还容易遇到命令行报错、系统拦截、文件缺…

2026/9/3 0:02:06

实测 OpenClaw 一键包,5 分钟完成本地自动化环境搭建

OpenClaw 本地 AI 自动化工具部署指南&#xff5c;使用一键包规避环境配置难题 痛点&#xff1a;部署 AI 自动化工具常常要处理 Python、Node.js 各类依赖&#xff0c;版本冲突、环境配置耗费大量时间&#xff0c;OpenClaw 提供一键安装包&#xff0c;降低部署门槛。 适配系统&…

2026/9/2 1:15:22

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行&#xff0c;Type-C接口算是典型的“看着简单&#xff0c;做起来全坑”的东西。光引脚就24个&#xff0c;高低速信号、电源、控制线全部塞在一个小小的连接器里&#xff0c;如果PCB布局不做规划&#xff0c;打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/2 1:15:22

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/2 1:15:20

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…