C 嵌入式系统设计模式 20:队列模式

本书的原著为:《Design Patterns for Embedded Systems in C ——An Embedded Software Engineering Toolkit 》,讲解的是嵌入式系统设计模式,是一本不可多得的好书。

本系列描述我对书中内容的理解。本文章描述嵌入式并发和资源管理模式之六:队列模式。

队列模式 (Queuing Pattern) 是一种 任务协作模式。在软件设计中,任务协作模式是用于协调不同任务之间通讯和同步的策略。它旨在确保任务能够高效、有序地执行,并处理任务之间的依赖关系、优先级冲突和资源共享等问题。

队列模式是任务间 异步通信 的最常见实现方式。它提供了一种简单的通信手段,适用于在时间上不耦合的任务之间的交互。队列模式通过将消息存储在队列(通常遵循先入先出原则的数据结构)中来实现这种通信。发送者将消息存入队列,并在稍后的某个时间点,接收者从队列中提取消息。由于队列的先入先出(FIFO)特性,任务可以按照消息发送的顺序来处理它们,这有助于维护系统的顺序和一致性

此外,队列模式还提供了一种简单的 序列化访问 共享资源的方法。访问消息被排队并在稍后的时间处理,这避免了共享资源时常见的互斥问题。通过将请求排入队列,系统可以有序地处理这些请求,从而避免资源冲突和竞争条件。

时间上不耦合的任务 指的是那些在执行时间上没有严格依赖或关联性的任务。这种任务可以独立地安排和执行,它们的完成时间不需要与其他任务同步或对齐。时间上的不耦合意味着任务之间不需要等待或协调彼此的执行进度。

摘要

消息队列模式用于异步通信,通过将消息 入队 ,实现任务间的同步和信息共享。这种方法简单,并且没有互斥问题,因为使用资源的任务与资源没有直接关系。线程之间共享的任何信息都是通过队列传递的。发送方将信息复制到队列,接收方完成拥有它接收到的信息,因此可以自由的修改它们,而不用担心以下情况的共享数据损坏:

  1. 多个写入者
  2. 一个写入者和多个读取者

队列模式的一个缺点是,信息不会在发送者发布后立即处理;进程会等待,直到接收任务运行,并能够处理等待的信息。

问题

在多线程系统中,任务必须与其他任务 同步 以及 共享信息,在这个过程中,必须要做到:

  1. 任务必须同步:在多线程环境中,多个线程可能同时访问和修改共享资源,如果不同线程对共享资源的访问和修改没有得到适当的同步,就可能导致数据不一致或其他未定义的行为。同步机制 可以确保在任何时候只有一个任务可以访问共享资源,或者至少确保访问是以一种可预测和可控的顺序进行的。因此,任务必须有同步机制,才能安全的共享资源。
  2. 资源的共享方式必须确保不会出现数据损坏或竞态条件:为了避免竞态条件和数据损坏,信息的共享方式需要经过精心设计。例如,可以使用原子操作来确保对共享数据的读取和修改是不可分割的 ( 临界区模式 );可以使用读写锁来允许多个任务同时读取共享数据,但只允许一个任务写入 ( 保护调用模式 );

而本文讲述的 队列模式 ,是解决此类任务 同步 以及 共享信息 的又一种策略。

竞态条件 是指两个或多个线程在时间上以一种不可预测的方式访问共享数据,导致程序的行为取决于线程的相对执行顺序。

模式结构

模式结构如下图所示:
在这里插入图片描述

其中,队列数量 决定了队列可以保留的最大元素数量。在实际应用中,需要特别注意确保这个大小足够大,以处理系统使用中的最坏情况,同时又不能太大以至于浪费内存。

模式详情

消息

消息类 是使用消息队列的多个 抢占式任务 之间相关消息的一种抽象。可以是简单的数据值,也可以是 TCP/IP 消息传递中使用的复杂数据报结构。

消息队列

消息队列 是一种存储结构,用于抢占式任务之间交换信息。消息队列能够存储消息数量是有限的。为了实现消息存储,消息队列通常提供以下方法:

  • int getNextIndex(MessageQueue* me, int index) :私有函数,使用模运算来计算下一个有效索引。
  • unsigned char isFull(MessageQueue* me):私有函数,消息队列 则返回 1 ,否则返回 0 。
  • unsigned char isEmpty(MessageQueue* me):私有函数,消息队列 则返回 1 ,否则返回 0 。
  • int insert(MessageQueue* me, Message m):公有函数,如果消息队列未满,调用该函数会向队列 head 指向的位置插入一个消息并更新 head 索引。如果插入消息成功,则返回 0 ,消息队列满则返回 1 。
  • Message* remove(MessageQueue* me):公有函数,如果消息队列非空,调用该函数先申请一个新的内存保存最老的消息,然后再把这个消息从队列中移除,之后更新 tail 索引,返回新申请内存的指针。如果函数执行失败返回 NULL

互斥量

互斥量 用于对 消息队列 的访问进行 序列化。当一个任务调用消息队列的受保护函数时,受保护函数内部会调用互斥锁的 lock() 函数,并在服务完成后调用 release() 函数。当互斥锁锁定时,如果有其他任务试图调用服务,这些任务将被阻塞,直到互斥锁解锁。互斥量通常由实时操作系统(RTOS)提供。

抢占式任务

抢占式任务 使用消息队列,它调用消息队列提供的 insert()remove() 函数来访问存储在其中的数据。这些操作分别用于向消息队列中添加数据或从中移除数据。通过这种方式,抢占式任务能够与消息队列进行交互,实现数据的共享和通信。

效果

队列模式 为任务间的数据传递提供了一种有效的访问序列化机制。通过互斥锁的使用,消息队列能够安全地应对多个任务的并发访问,确保数据的完整性和一致性。由于队列模式实现了异步通信,因此,与 保护调用模式 相比,可能存在一定的延迟。然而,这种延迟是为了实现更好的任务解耦和资源利用率,允许数据发送者和接收者以独立的速率运行,从而提高了系统的整体性能和灵活性。

队列的容量可以设计得相当庞大,这种设计在处理突发性数据生成时显得尤为有用。它允许数据的消费者在生成高峰过后,按自己的节奏进行处理。然而,队列大小的设置需要谨慎考虑:过小的队列可能无法容纳突发的数据流量,从而导致宝贵的数据丢失;而过大的队列则可能占用过多的内存资源,造成不必要的浪费。另一方面,对于简单的异步数据交换场景,一个非常小的队列(例如只能容纳一两个元素)可能就足够了,因为在这种情境下通常不会出现数据丢失的问题。因此,根据具体的应用场景和需求来合理设置队列大小是至关重要的。

实现策略

最近的实现策略是使用数组,但这种方式没有链表灵活。

队列的实现相当简单,但却有着多种变体以适应不同的需求。在某些情况下,一些消息因其紧急性或重要性需要优先处理,这就要求队列能够支持优先级排序。为了实现这一点,可以通过增加多个缓冲区,每个对应不同的优先级,或者根据消息的优先级在队列中动态地插入元素。这样的设计修改使得高优先级的消息能够在低优先级消息之前得到处理。然而,这也可能带来一定的性能开销,如增加元素插入和删除的时间。因此,在实现优先级队列时,消息应当携带一个优先级属性,以便队列能够依据这一属性进行正确的排序和操作。这样的设计确保了系统既能够处理常规的消息流,又能够迅速响应紧急或重要的消息。

在复杂系统中,由于各种不确定性和动态性,最佳队列大小可能难以预测。为了解决这个问题,可以实现一种可扩展队列。当消息队列满时,这种队列能够动态地分配更多内存以容纳新消息。链表实现方式由于其灵活性,特别适合用于可扩展队列。然而,确定何时缩小队列并释放未使用的内存是一个挑战。一种可能的策略是监控队列的使用情况,当队列长时间处于低使用率状态时,考虑缩小其大小。但需要注意的是,频繁地调整队列大小可能会引入额外的开销和复杂性。

当潜在的元素数量超出内存容量时,缓存队列成为了一种有效的解决方案。这种队列结构主要包括三个存储组件:本地内存中的最新数据缓冲区、最旧数据缓冲区,以及用于存储中间数据的文件(比如存储到硬盘中)。当新数据缓冲区填满时,数据会被转移至文件系统进行存储。一旦旧数据缓冲区为空,系统会从文件系统中读取数据填充进去,并随后从磁盘中删除这些已经读取的数据(或者,在文件为空的情况下,从新数据缓冲区中复制数据)。这样的设计使得存储大量数据成为可能,但需要注意的是,缓存数据的操作可能会相当耗时 (因为可能要读写磁盘,而读写磁盘操作相比读写内存来说要慢的多),因此在设计系统时需要权衡存储需求和性能要求。

相关模式

队列模式 通过数据或命令的队列化来实现对数据的序列化访问,确保接收者能够按顺序逐一处理。由于队列模式是 异步的 ,发送消息和处理消息之间的时间被解耦,这提供了系统各组件间的灵活性,但也可能无法满足对实时性要求较高的系统的性能需求。

相比之下,保护调用模式 也实现了序列化访问,但它是 同步 进行的。在这种模式下,数据和命令的传输在时间上通常更加紧凑,适用于需要实时响应的场景。然而,如果不当使用,保护调用模式可能会引发不受控制的优先级反转问题。

此外,在保护调用模式中,如果接收者尚未准备好接收来自发送者的消息,发送者必须选择阻塞等待或采取其他措施,如重试、超时返回或放弃发送。这可能会增加系统的复杂性和开销。

实例

见原书。






读后有收获,资助博主养娃 - 千金难买知识,但可以买好多奶粉 (〃‘▽’〃)
千金难买知识,但可以买好多奶粉

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

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

相关文章

Stable Diffusion WebUI API http://127.0.0.1:7860/docs空白

在尝试调用Stable Diffusion WebUI API的时候,打开http://127.0.0.1:7860/docs遇到了以下页面 网络诊断是这样的原因: 修bug,改来改去遇到了以下两种页面: 此时http://127.0.0.1:7860可以如下正常显示: 查资料的时候找…

外包干了10天,技术退步明显。。。。。

先说一下自己的情况,本科生,2019年我通过校招踏入了南京一家软件公司,开始了我的职业生涯。那时的我,满怀热血和憧憬,期待着在这个行业中闯出一片天地。然而,随着时间的推移,我发现自己逐渐陷入…

域名 DNS 信息查询 API 数据接口

域名 DNS 信息查询 API 数据接口 网络工具,多种记录类型数据返回,丰富的信息结构,毫秒级响应。 1. 产品功能 提供域名 DNS 解析完整记录;丰富的解析记录类型,包括:A, AAAA, MX, TXT, NS, CNAME, SRV, PTR…

SPI 接口

SPI 接口 SPI 简介寻址方式通信过程极性和相位IIC 和 SPI 的异同相同点不同点 SPI 简介 SPI(Serial Peripheral Interface)是串行外设接口的缩写,SPI是一种高速的、全双工、同步的串行通信总线;SPI采用主从方式工作,一…

强大的ps 命令 -o 自定义输出内容选项

强大的ps 命令 -o 自定义输出内容选项 1、ps命令介绍和作用2、问题描述 1、ps命令介绍和作用 ps 是一个 Unix 和类 Unix 操作系统中常用的命令,用于显示当前运行的进程信息。ps 命令的作用包括: 查看进程信息: ps 命令可以列出当前系统中正…

数据结构——lesson5栈和队列详解

hellohello~这里是土土数据结构学习笔记🥳🥳 💥个人主页:大耳朵土土垚的博客 💥 所属专栏:数据结构学习笔记 💥对于顺序表链表有疑问的都可以在上面数据结构的专栏进行学习哦~感谢大家的观看与…

JavaScript实现点击鼠标弹钢琴的效果

思路&#xff1a; 图片设置宽900px&#xff0c;找到鼠标按下时的x坐标和img距离body的x坐标&#xff0c;两个值相减&#xff0c;然后除100取整&#xff0c;赋值给a&#xff0c;通过判断a的值来确定放出那个音乐。 完整代码&#xff1a; <!DOCTYPE html> <html lan…

YOLOv9独家原创改进|使用HWD:Haar小波下采样模块

专栏介绍&#xff1a;YOLOv9改进系列 | 包含深度学习最新创新&#xff0c;主力高效涨点&#xff01;&#xff01;&#xff01; 一、论文简介 最大池化或跨步卷积等下采样操作在卷积神经网络&#xff08;CNNs&#xff09;中广泛使用&#xff0c;以聚合局部特征&#xff0c;扩大感…

Cobalt Strike 4.9.1(已更新,文章图片没换)

Cobalt Strike 4.9.1 1. 工具介绍1.1. 工具添加1.2. 工具获取 2. 工具使用2.1. 添加权限并运行2.2. 连接服务端2.3. 连接成功 3. 安全性自查 1. 工具介绍 CS 是Cobalt Strike的简称&#xff0c;是一款渗透测试神器&#xff0c;常被业界人称为CS神器。Cobalt Strike已经不再使用…

用ChatGPT计算植被归一化指数NDVI并出图的详细教程

用ChatGPT结合GIS计算植被归一化指数NDVI出图教程 用ENVI计算比较繁琐&#xff0c;如今AI的盛行&#xff0c;我们可以轻松解决计算问题&#xff0c;只需1一分钟变可以出图。 详细教学请看上方视频步骤。 更多ChatGPT教学内容请见&#xff1a;ChatGPT结合GIS&#xff1a;一分钟…

SpringBoot+Mybatis-plus+shardingsphere实现分库分表

SpringBootMybatis-plusshardingsphere实现分库分表 文章目录 SpringBootMybatis-plusshardingsphere实现分库分表介绍引入依赖yaml配置DDL准备数据库ds0数据库ds1 entitycotrollerserviceMapper启动类测试添加修改查询删除 总结 介绍 实现亿级数据量分库分表的项目是一个挑战…

C++之获取Windows系统信息

目录 1. 操作系统版本 2. 获取CPU信息 3. 获取内存信息 4. 获取硬盘信息 5.获取网络接口信息 6.获取计算机名称、用户名 在C中&#xff0c;你可以使用Windows API函数来获取Windows系统的各种信息。以下是一些常见的API函数和示例代码&#xff0c;用于获取Windows系统信息…

Nerf原理理解

神经辐射场是一个简单的全连接网络&#xff08;权重约为 5MB&#xff09;&#xff0c;经过训练可使用渲染损失再现单个场景的输入视图。该网络直接从空间位置和观看方向&#xff08;5D 输入&#xff09;映射到颜色和不透明度&#xff08;4D 输出&#xff09;&#xff0c;充当“…

【Kafka系列 06】Kafka Producer源码解析

温馨提示&#xff1a;本文基于 Kafka 2.3.1 版本。 一、Kafka Producer 原理图 生产者的 API 使用还是比较简单&#xff0c;创建一个 ProducerRecord 对象&#xff08;这个对象包含目标主题和要发送的内容&#xff0c;当然还可以指定键以及分区&#xff09;&#xff0c;然后调…

全方位碾压chatGPT4的全球最强模型Claude 3发布!速通指南在此!保姆级教学拿脚都能学会!

&#x1f389;&#x1f389;欢迎光临&#xff0c;终于等到你啦&#x1f389;&#x1f389; &#x1f3c5;我是苏泽&#xff0c;一位对技术充满热情的探索者和分享者。&#x1f680;&#x1f680; &#x1f31f;持续更新的专栏《Spring 狂野之旅&#xff1a;从入门到入魔》 &a…

李沐动手学习深度学习——3.5练习

减少batch_size&#xff08;如减少到1&#xff09;是否会影响读取性能&#xff1f; 肯定会影响&#xff0c;计算机io性能而言&#xff0c;随着batch_size增大&#xff0c;读取越来越快&#xff0c;需要的时间越少。这里会涉及到计算机操作系统的知识点&#xff0c;内存与硬盘之…

第五节 JDBC驱动程序类型

JDBC驱动程序是什么&#xff1f; JDBC驱动程序在JDBC API中实现定义的接口&#xff0c;用于与数据库服务器进行交互。 例如&#xff0c;使用JDBC驱动程序&#xff0c;可以通过发送SQL或数据库命令&#xff0c;然后使用Java接收结果来打开数据库连接并与数据库进行交互。 JDK…

【2024】vue-router和pinia的配置使用

目录 vue-routerpiniavue-routerpinia进阶用法---动态路由 有同学在项目初始化后没有下载vue-router和pinia&#xff0c;下面开始&#xff1a; vue-router npm install vue-router然后在src目录下创建文件夹router&#xff0c;以及下面的index.ts文件&#xff1a; 写进下面的…

华为智慧教室3.0的晨光,点亮教育智能化变革

“教室外有更大的世界&#xff0c;但世界上没有比教室更伟大的地方。” 我们在求学阶段&#xff0c;都听说过这句话&#xff0c;但往往是在走出校园之后&#xff0c;才真正理解了这句话。为了让走出校园的孩子能够有能力&#xff0c;有勇气探索广阔的世界。我们应该准备最好的教…

碳视野|全国首个ESG区域行动方案通过,上海政府推进ESG有八“要”

引领绿色转型&#xff0c;共筑低碳未来&#xff01;AMT企源碳管理团队深入解读碳领域政策、概念及标准&#xff0c;分享实践经验&#xff0c;助力产业绿色发展。我们启动“碳视野、碳课堂、碳实践”三大专栏&#xff0c;紧跟碳行业政策动态&#xff0c;以“科普实践分享”为核心…