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

Sparrow系列拓展篇:对信号量应用问题的深入讨论

前言

笔者之前已经介绍过了Sparrow信号量的源码,但是对于信号量的使用,并没有讲得非常详细,仅仅讲了同步与互斥的概念。

本章让笔者介绍如何使用Sparrow的信号量,深入探讨一下信号量在同步、计数与互斥中的应用。

使用信号量解决资源问题

生产者,消费者是一种经典模型,一边是供给,一边是需求。在计算机历史上,有许多这种问题,例如哲学家进餐问题,有兴趣的读者可以自行研究。

本篇文章中,我们主要解决以下问题:

单生产者,单消费者

多生产者,单消费者

多生产者,多消费者

多读者,多写者

单生产者,单消费者

我们通过创建环形缓冲区(一种特殊的队列)来建立生产者和消费者模型,一个插入消息,另一个获取消息。

观察结构体,有两个信号量,一个初始化时用于同步,一个用于计数。

只要分配合理的优先级,信号量可以用于互斥,与互斥锁相比,它的优点在于没有所有者的概念,完全可以让两个不同的线程形成原子操作。

看看具体的代码:

Oo_insert函数是生产者:先使用计数信号量获取是否有空间,然后插入数据,然后释放信号通知消费者。

Oo_remove函数是消费者:先获取是否有数据,再取出数据,然后恢复计数。

#define BufferSIZE 5
Class(Oo_buffer)
{int buf[BufferSIZE];int in;int out;Semaphore_Handle item;Semaphore_Handle space;
};Oo_buffer_handle Oo_buffer_creat(void)
{Oo_buffer_handle Oo_buffer1 = heap_malloc(sizeof (Oo_buffer));*Oo_buffer1 = (Oo_buffer){.buf = {0,0,0,0,0},.in  = 0,.out = 0,.item = semaphore_creat(0),.space = semaphore_creat(BufferSIZE)};return Oo_buffer1;
}void Oo_insert(Oo_buffer_handle Oo_buffer1, int object)
{semaphore_take(Oo_buffer1->space, 1);Oo_buffer1->buf[Oo_buffer1->in] =  object;Oo_buffer1->in = (Oo_buffer1->in + 1) % BufferSIZE;semaphore_release(Oo_buffer1->item);
}int Oo_remove(Oo_buffer_handle Oo_buffer1)
{semaphore_take(Oo_buffer1->item, 1);int item1 = Oo_buffer1->buf[Oo_buffer1->out];Oo_buffer1->out = (Oo_buffer1->out + 1) % BufferSIZE;semaphore_release(Oo_buffer1->space);return item1;
}

多生产者,单消费者

多生产者时,与单生产者相比只有一个地方需要注意,那就是对环形缓冲区本身的访问,如果仍然使用单生产者的模型,多个生产者可以同时获取计数信号量,但是它们的下标操作会导致竞态的发生。

所以我们额外需要一个信号量,用于保证生产者们互斥访问环形缓冲区。

Class(Mo_buffer)
{int buf[BufferSIZE];int in;int out;Semaphore_Handle item;Semaphore_Handle space;Semaphore_Handle guard;
};Mo_buffer_handle Mo_buffer_creat(void)
{Mo_buffer_handle Mo_buffer1 = heap_malloc(sizeof (Mo_buffer));*Mo_buffer1 = (Mo_buffer){.buf = {0,0,0,0,0},.in  = 0,.out = 0,.item = semaphore_creat(0),.space = semaphore_creat(BufferSIZE),.guard = semaphore_creat(1)};return Mo_buffer1;
}void Mo_insert(Mo_buffer_handle Mo_buffer1, int object)
{semaphore_take(Mo_buffer1->space, 1);semaphore_take(Mo_buffer1->guard, 1),Mo_buffer1->buf[Mo_buffer1->in] =  object;Mo_buffer1->in = (Mo_buffer1->in + 1) % BufferSIZE;semaphore_release(Mo_buffer1->guard);semaphore_release(Mo_buffer1->item);
}int Mo_remove(Mo_buffer_handle Mo_buffer1)
{semaphore_take(Mo_buffer1->item, 1);int item1 = Mo_buffer1->buf[Mo_buffer1->out];Mo_buffer1->out = (Mo_buffer1->out + 1) % BufferSIZE;semaphore_release(Mo_buffer1->space);return item1;
}

多生产者,多消费者

多消费者同理,我们额外需要一个信号量,用于保证消费者们互斥访问环形缓冲区。

Class(Mm_buffer)
{int buf[BufferSIZE];int in;int out;Semaphore_Handle item;Semaphore_Handle space;Semaphore_Handle guard;
};Mm_buffer_handle Mm_buffer_creat(void)
{Mm_buffer_handle Mm_buffer1 = heap_malloc(sizeof (Mm_buffer));*Mm_buffer1 = (Mm_buffer){.buf = {0,0,0,0,0},.in  = 0,.out = 0,.item = semaphore_creat(0),.space = semaphore_creat(BufferSIZE),.guard = semaphore_creat(1),};return Mm_buffer1;
}void Mm_insert(Mm_buffer_handle Mm_buffer1, int object)
{semaphore_take(Mm_buffer1->space, 1);semaphore_take(Mm_buffer1->guard, 1),Mm_buffer1->buf[Mm_buffer1->in] =  object;Mm_buffer1->in = (Mm_buffer1->in + 1) % BufferSIZE;semaphore_release(Mm_buffer1->guard);semaphore_release(Mm_buffer1->item);
}int Mm_remove(Mm_buffer_handle Mm_buffer1)
{semaphore_take(Mm_buffer1->item, 1);semaphore_take(Mm_buffer1->guard, 1);int item1 = Mm_buffer1->buf[Mm_buffer1->out];Mm_buffer1->out = (Mm_buffer1->out + 1) % BufferSIZE;semaphore_release(Mm_buffer1->guard);semaphore_release(Mm_buffer1->space);return item1;
}

多读者,多写者

多读多写问题稍微复杂一些,我们需要两个互斥信号量,两个同步信号量,两个读者计数值,两个写者计数值。

顺便一提,笔者这里讲的互斥信号量不是互斥锁。Sparrow中互斥信号量是初始化时value为1的信号量,它的优点在于没有所有者的概念。

读模型:

申请读:先声明为读者,获取互斥信号量保证对计数值的互斥,如果没有活跃的写者,那么获取访问权限,否则等待读取信号量。

读完释放:减少计数值,如果没有正在读的读者,但是存在等待的写者,那么唤醒它们。

写模型:

申请写:先声明为写者,如果没有正在读的读者,那么成为写者,然后获取互斥信号量保证申请写以及写完释放操作的原子性

写完释放:获取互斥信号量,减少计数,如果没有其他活跃者,但是存在等待的读者,那么增加正在读的读者的计数,然后唤醒这些读者。

Class(MrMw_control)
{Semaphore_Handle read;Semaphore_Handle write;Semaphore_Handle W_guard;Semaphore_Handle C_guard;int active_reader;int reading_reader;int active_writer;int writing_writer;
};MrMw_control_handle MrOw_creat(void)
{MrMw_control_handle MrMw_control1 = heap_malloc(sizeof (MrMw_control));*MrMw_control1 = (MrMw_control){.read = semaphore_creat(0),.write = semaphore_creat(0),.W_guard = semaphore_creat(1),.C_guard = semaphore_creat(1),.active_reader = 0,.reading_reader = 0,.active_writer = 0,.writing_writer = 0};return MrMw_control1;
}void read_acquire(MrMw_control_handle MrMw_control1)
{semaphore_take(MrMw_control1->C_guard, 1);MrMw_control1->active_reader += 1;if(MrMw_control1->active_writer == 0){MrMw_control1->reading_reader += 1;semaphore_release(MrMw_control1->read);}semaphore_release(MrMw_control1->C_guard);semaphore_take(MrMw_control1->read, 1);
}void read_release(MrMw_control_handle MrMw_control1)
{semaphore_take(MrMw_control1->C_guard, 1);MrMw_control1->reading_reader -= 1;MrMw_control1->active_reader -= 1;if(MrMw_control1->reading_reader == 0){while(MrMw_control1->writing_writer < MrMw_control1->active_writer){MrMw_control1->writing_writer += 1;semaphore_release(MrMw_control1->write);}}semaphore_release(MrMw_control1->C_guard);
}void write_acquire(MrMw_control_handle MrMw_control1)
{semaphore_take(MrMw_control1->C_guard, 1);MrMw_control1->active_writer -= 1;if(MrMw_control1->reading_reader == 0){MrMw_control1->writing_writer += 1;semaphore_release(MrMw_control1->write);}semaphore_release(MrMw_control1->C_guard);semaphore_take(MrMw_control1->write, 1);semaphore_take(MrMw_control1->W_guard, 1);
}void write_release(MrMw_control_handle MrMw_control1)
{semaphore_release(MrMw_control1->W_guard);semaphore_take(MrMw_control1->C_guard, 1);MrMw_control1->writing_writer -= 1;MrMw_control1->active_writer -= 1;if(MrMw_control1->active_writer == 0){while(MrMw_control1->reading_reader < MrMw_control1->active_reader){MrMw_control1->reading_reader += 1;semaphore_release(MrMw_control1->read);}}semaphore_release(MrMw_control1->C_guard);
}

总结

笔者讲解了如何使用信号量解决生产者和消费者、多读多写者问题。

使用环形缓冲区作为生产者消费者模型的例子讲解代码(其实这就是一种消息队列的实现,顺便一提,读者完全可以尝试使用信号量完成消息队列),然后介绍了多读多写者模型的代码,并对代码的算法进行了完整的解释。

源码在Sparrow文件夹的demo里面:skaiui2/SKRTOS_sparrow at source

相关文章:

Sparrow系列拓展篇:对信号量应用问题的深入讨论

前言 笔者之前已经介绍过了Sparrow信号量的源码&#xff0c;但是对于信号量的使用&#xff0c;并没有讲得非常详细&#xff0c;仅仅讲了同步与互斥的概念。 本章让笔者介绍如何使用Sparrow的信号量&#xff0c;深入探讨一下信号量在同步、计数与互斥中的应用。 使用信号量解…...

图文详解Docker下配置、测试Redis

文章目录 前言实测环境&#xff1a;实验思路&#xff1a; 正文1.准备工作2. 配置、运行 Redis 容器3. 配置测试 总结 前言 配置、测试redis数据库服务器&#xff0c;首先确保正确安装docker&#xff0c;并且已启动运行&#xff0c;具体安装docker方法见笔者前面的博文《OpenEu…...

Python编程艺术:优雅与实用的完美平衡(推导式)

在Python这门优雅的编程语言中&#xff0c;处处体现着"简洁即是美"的设计哲学。今天我们深入探讨Python中那些让代码更优雅、更高效的编程技巧&#xff0c;这些技巧不仅能提升代码的可读性&#xff0c;还能让编程过程充满乐趣。 列表推导式的魔力 Python的列表推导…...

Spring Boot框架Starter组件整理

在Spring Boot框架中&#xff0c;starter是一种预定义的依赖集合&#xff0c;旨在简化Maven或Gradle等构建工具中的依赖管理。每个starter都包含了实现特定功能所需的库和组件&#xff0c;以及相应的配置文件。开发者只需在项目中引入相应的starter依赖&#xff0c;即可快速搭建…...

C/C++基础知识复习(27)

1) 移动语义和拷贝语义的区别 拷贝语义和移动语义是C中对象所有权管理的两种机制&#xff0c;主要在对象初始化、赋值或传参时体现。 拷贝语义 (Copy Semantics) 行为&#xff1a;通过深拷贝或浅拷贝&#xff0c;创建一个新对象&#xff0c;并将原对象的值或资源复制到新对象…...

IEC61850实现方案和测试-2

IEC61850实现方案和测试-1作为介绍实现方案和测试的第二篇文章&#xff0c;后续会继续更新&#xff0c;欢迎关注。 第一篇是&#xff1a;IEC61850实现方案和测试-1-CSDN博客 UCA详细测试用例下载&#xff1a; 链接: https://pan.baidu.com/s/1TTMlYRfzKITgrkWwwtcrDg 提取码:…...

flume-将日志采集到hdfs

看到hdfs大家应该做什么&#xff1f; 是的你应该去把集群打开&#xff0c; cd /export/servers/hadoop/sbin 启动集群 ./start-all.sh 在虚拟机hadoop02和hadoop03上的conf目录下配置相同的日志采集方案&#xff0c;‘ cd /export/servers/flume/conf 切换完成之后&#…...

一文学习开源框架LeakCanary

LeakCanary 简介 LeakCanary 是一个由 Square 开发的开源工具&#xff0c;主要用于检测和诊断 Android 应用中的内存泄漏问题。它通过自动化的方式帮助开发者捕捉和分析可能导致内存泄漏的对象&#xff0c;简化了内存问题的排查过程。 LeakCanary 的功能 自动检测内存泄漏&a…...

jetson orin系列开发版安装cuda的gpu版本的opencv

opencv安装包下载地址&#xff1a; https://github.com/opencv/opencv/扩展库下载地址&#xff1a; https://github.com/opencv/opencv_contrib1. 删除jetpack包中的opencv版本 原先的opencv库安装在目录/usr/lib/aarch64-linux-gnu/下&#xff08;一般其他的第三方库也都安…...

数据结构-8.Java. 七大排序算法(中篇)

本篇博客给大家带来的是排序的知识点, 由于时间有限, 分两天来写, 中篇主要实现后三种排序算法: 冒泡排序,快速排序,下一篇讲 归并排序. 文章专栏: Java-数据结构 若有问题 评论区见 欢迎大家点赞 评论 收藏 分享 如果你不知道分享给谁,那就分享给薯条. 你们的支持是我不断创作…...

数据结构C语言描述4(图文结合)--栈的实现,中序转后序表达式的实现

前言 这个专栏将会用纯C实现常用的数据结构和简单的算法&#xff1b;有C基础即可跟着学习&#xff0c;代码均可运行&#xff1b;准备考研的也可跟着写&#xff0c;个人感觉&#xff0c;如果时间充裕&#xff0c;手写一遍比看书、刷题管用很多&#xff0c;这也是本人采用纯C语言…...

python基本数据类型 -- 元组tuple

在 Python 中&#xff0c;元组&#xff08;Tuple&#xff09;是一种轻量级的、不可变的数据结构。与列表类似&#xff0c;元组用于存储有序的数据集合&#xff0c;但它一旦创建&#xff0c;其内容就无法更改。这种特性让元组在某些场景下更加安全和高效。本文将从定义、操作、应…...

tcpdump交叉编译

TCPDUMP在Libpcap上开发。 首先需要编译libcap。 网上那么多教程&#xff0c;下载地址都只给了一个英文的官网首页&#xff0c; 你尽可以试试&#xff0c;从里面找到下载地址都要费半天时间。 \color{red}网上那么多教程&#xff0c;下载地址都只给了一个英文的官网首页&#…...

Spring IOC注入方式、Bean作用域

Spring IOC注入 手动注入 set方法注入 需要提供set方法 public class UserService {private UserDao userDao; ​public void setUserDao(UserDao userDao) {this.userDao userDao;} } 设置属性字段的值 <bean id"userService" class"com.shsxt.servi…...

uniapp微信小程序转发跳转指定页面

onShareAppMessage 是微信小程序中的一个重要函数&#xff0c;用于自定义转发内容。当用户点击右上角的菜单按钮&#xff0c;并选择“转发”时&#xff0c;会触发这个函数。开发者可以在这个函数中返回一个对象&#xff0c;用于定义分享卡片的标题、图片、路径等信息。 使用场…...

利用uniapp开发鸿蒙:运行到鸿蒙模拟器—踩坑合集

从uniapp运行到鸿蒙模拟器上这一步&#xff0c;就有非常多的坑&#xff0c;一些常见的坑&#xff0c;官网都有介绍&#xff0c;就不再拿出来了&#xff0c;这里记录一下官网未记录的大坑 1.运行路径从hbuilderx启动鸿蒙模拟器 解决方法&#xff1a; Windows系统&#xff0c;官…...

【Vue】Vue3.0(二十五)Vue3.0中的具名插槽 的概念和使用场景

上篇文章 【Vue】Vue3.0&#xff08;二十四&#xff09;Vue3.0中 r e f s 、 refs 、 refs、parent 的概念和使用场景 &#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f916;Vue专栏&#xff1a;点击&#xff01; ⏰️创作时间&#xff1a;2024年11月20日16点30分 …...

【pytorch-02】:张量的索引、形状操作和常见运算函数

文章目录 1 张量索引1.1 简单行列索引和列表索引1.2 布尔索引和多维索引 2 张量的形状操作2.1 reshape函数2.2 transpose和permute函数的使用2.3 view和contiguous函数2.4 squeeze和unsqueeze函数用法2.5 张量更改形状小结 3 常见运算函数 1 张量索引 1.1 简单行列索引和列表索…...

C语言-指针作为函数返回值及二级指针

1、指针作为函数返回值 c语言允许函数的返回值是一个指针&#xff08;地址&#xff09;我们将这样的函数称为指针函数&#xff0c;下面的例子定义一了一个函数strlong&#xff08;&#xff09;&#xff0c;用来返回两个字符串中较长的一个&#xff1a; 1. #include <stdio…...

css 使用图片作为元素边框

先看原始图片 再看效果 边框的四个角灭有拉伸变形,但是图片的中部是拉伸的 代码 border-style: solid;/* 设置边框图像的来源 */border-image-source: url(/static/images/mmwz/index/bk_hd3x.png);/* 设置如何切割图像 */border-image-slice: 66;/* 设置边框的宽度 */border…...

终极M3U8视频下载指南:5个技巧轻松掌握开源工具

终极M3U8视频下载指南&#xff1a;5个技巧轻松掌握开源工具 【免费下载链接】N_m3u8DL-CLI-SimpleG N_m3u8DL-CLIs simple GUI 项目地址: https://gitcode.com/gh_mirrors/nm3/N_m3u8DL-CLI-SimpleG 想要下载在线视频却总是失败&#xff1f;面对M3U8格式束手无策&#x…...

告别焦虑等待!Elsevier投稿状态自动追踪插件,让你的科研进度一目了然

告别焦虑等待&#xff01;Elsevier投稿状态自动追踪插件&#xff0c;让你的科研进度一目了然 【免费下载链接】Elsevier-Tracker 项目地址: https://gitcode.com/gh_mirrors/el/Elsevier-Tracker 还在每天刷新Elsevier投稿页面&#xff0c;只为查看论文审稿状态吗&…...

127、运动控制中的硬件抽象层设计

运动控制中的硬件抽象层设计 从一次电机“鬼畜”说起 去年调试一个四轴协作机器人,电机在低速运行时突然出现周期性抖动,示波器抓出来一看,电流波形每隔几十毫秒就出现一个毛刺。排查了三天,最后发现是底层驱动库里的定时器中断优先级被某个外设库给改了——硬件抽象层(…...

Palantir 现在干的活,本质上就是你描述的那个方向,但它在“深度”和“广度”上比你目前的 MVP 设想走得更远。如果说你想做的是一个“能听懂人话的 SQL 查询工具”,那么 Palantir

Palantir 现在干的活&#xff0c;本质上就是你描述的那个方向&#xff0c;但它在“深度”和“广度”上比你目前的 MVP 设想走得更远。如果说你想做的是一个“能听懂人话的 SQL 查询工具”&#xff0c;那么 Palantir 构建的是一个 “企业级的数字孪生操作系统”。它不仅仅是在“…...

原神帧率解锁工具:如何安全突破60FPS限制获得流畅体验

原神帧率解锁工具&#xff1a;如何安全突破60FPS限制获得流畅体验 【免费下载链接】genshin-fps-unlock unlocks the 60 fps cap 项目地址: https://gitcode.com/gh_mirrors/ge/genshin-fps-unlock 《原神》作为一款画面精美的开放世界游戏&#xff0c;默认的60FPS帧率限…...

Unity ShaderGraph实战指南:从美术协作到URP渲染优化

1. 为什么我劝新手别急着写Shader代码——从一个被美术追着问“这个效果能不能加”的下午说起 去年冬天&#xff0c;我在一家做AR教育产品的团队里做技术美术。那天下午三点&#xff0c;UI组的同事抱着iPad冲进我工位&#xff1a;“老师&#xff0c;这个粒子光晕要加呼吸感&…...

Halcon形状匹配实战:从`get_domain`到`add_channels`,手把手教你处理复杂背景下的目标定位

Halcon形状匹配实战&#xff1a;从get_domain到add_channels的工业级解决方案 在工业视觉检测中&#xff0c;目标定位的准确性直接影响着整个生产线的质量把控效率。当面对低对比度、复杂背景或干扰物密集的场景时&#xff0c;传统全图搜索策略往往表现不佳——这正是Halcon区域…...

论文AI率90%熬夜怎么办?2026年5招实测,一次过知网维普AIGC

2025 年 12 月 25 日知网 AIGC 检测系统升级&#xff0c;2026 年 4 月 27 日维普 AI 率检测平台升级…2026 毕业季&#xff0c;各大主流 AIGC 检测软件陆续升级系统&#xff0c;识别 AI 痕迹更加精准。 临近毕业&#xff0c;同学们看者飘红的 AIGC 检测报告、纷繁复杂的降 AI 系…...

Sub-Zero字幕格式转换:从SRT到VTT的完整处理流程

Sub-Zero字幕格式转换&#xff1a;从SRT到VTT的完整处理流程 【免费下载链接】Sub-Zero.bundle Subtitles for Plex, as good you would expect them to be. 项目地址: https://gitcode.com/gh_mirrors/su/Sub-Zero.bundle Sub-Zero是一款为Plex媒体服务器提供高质量字幕…...

SpringBoot+Vue体育赛事志愿者管理系统源码+论文

代码可以查看文章末尾⬇️联系方式获取&#xff0c;记得注明来意哦~&#x1f339; 分享万套开题报告任务书答辩PPT模板 作者完整代码目录供你选择&#xff1a; 《SpringBoot网站项目》1800套 《SSM网站项目》1500套 《小程序项目》1600套 《APP项目》1500套 《Python网站项目》…...