同步与互斥与通信

news/2024/11/20 21:07:06/文章来源:https://www.cnblogs.com/lichungang/p/18542175

同步与互斥

同步:两任务要协调

互斥:两任务要争用

举一个例子。在团队活动里,同事A先写完报表,经理B才能拿去向领导汇报。经理B必须等同事A完成报表,AB之间有依赖,B必须放慢脚步,被称为同步。在团队活动中,同事A已经使用会议室了,经理B也想使用,即使经理B是领导,他也得等着,这就叫互斥。经理B跟同事A说:你用完会议室就提醒我。这就是使用"同步"来实现"互斥"。

 

仅仅使用简单的全局变量的方法实现同步与互斥是有缺陷的

可以实现同步与互斥的内核方法有:任务通知(task notification)、队列(queue)、事件组(event group)、信号量(semaphoe)、互斥量(mutex)。

  1. 队列(Queue)

    • 功能:用于在任务之间传递数据。队列是一种先进先出(FIFO)数据结构,可以存储一定数量的数据项。
    • 用途:适用于需要传递数据或消息的场景,例如生产者-消费者模式。任务可以将数据发送到队列中,另一个任务可以从队列中接收数据。队列可以存储任意类型的数据,并且支持阻塞操作,以便任务可以在队列为空时等待。
  2. 事件组(Event Group)

    • 功能:用于在多个任务之间传递事件或信号。这些事件可以是单个或多个标志位(bit),每个任务可以对这些标志位进行设置、清除或等待。
    • 用途:适合处理需要多个任务之间协调的场景。例如,一个任务可以等待多个事件的发生(例如,一个任务等待两个条件同时满足),而另一个任务可以设置这些事件,从而通知等待的任务。事件组也可以实现任务的优先级控制。
  3. 信号量(Semaphore)

    • 功能:用于控制对共享资源的访问。信号量主要有两种类型:二进制信号量和计数信号量。
    • 用途:二进制信号量通常用于实现互斥,确保同一时间只有一个任务可以访问特定资源。计数信号量允许多个任务同时访问资源,直到达到预定的最大值。信号量可以帮助避免竞态条件和数据不一致问题。
  4. 任务通知(Task Notification)

    • 功能:用于实现任务间的简单通知机制。在 FreeRTOS 中,每个任务都有一个相关的通知值,可以通过直接从一个任务通知另一个任务。
    • 用途:适合需要简单、低开销的通知机制的场景。任务可以使用通知值来发送较小的信息(如指示一个事件发生),并且可以选择使用阻塞或非阻塞方式等待通知。这种机制比队列更加轻量级。

队列

创建队列(动态、静态)

QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength, UBaseType_t uxItemSize );
参数说明
uxQueueLength 队列长度,最多能存放多少个数据(item)
uxItemSize 每个数据(item)的大小:以字节为单位
返回值 非0:成功,返回句柄,以后使用句柄来操作队列 NULL:失败,因为内存不足
QueueHandle_t xQueueCreateStatic(*UBaseType_t uxQueueLength,*UBaseType_t uxItemSize,*uint8_t *pucQueueStorageBuffer,*StaticQueue_t *pxQueueBuffer*);
参数说明
uxQueueLength 队列长度,最多能存放多少个数据(item)
uxItemSize 每个数据(item)的大小:以字节为单位
pucQueueStorageBuffer 指向一个用于存储队列的内存区域的指针,如果uxItemSize非0,pucQueueStorageBuffer必须指向一个uint8_t数组, 此数组大小至少为"uxQueueLength * uxItemSize"
pxQueueBuffer 必须执行一个StaticQueue_t结构体,用来保存队列的数据结构(控制信息)
返回值 非0:成功,返回句柄,以后使用句柄来操作队列 NULL:失败,因为pxQueueBuffer为NULL

读队列

使用 xQueueReceive() 函数读队列,读到一个数据后,队列中该数据会被移除。这个函数有两个版本:在任务中使用、在ISR中使用。函数原型如下:

BaseType_t xQueueReceive( QueueHandle_t xQueue,void * const pvBuffer,TickType_t xTicksToWait );BaseType_t xQueueReceiveFromISR(QueueHandle_t    xQueue,void             *pvBuffer,BaseType_t       *pxTaskWoken);
参数说明
xQueue 队列句柄,要读哪个队列
pvBuffer bufer指针,队列的数据会被复制到这个buffer 复制多大的数据?在创建队列时已经指定了数据大小
xTicksToWait 如果队列空则无法读出数据,可以让任务进入阻塞状态, xTicksToWait表示阻塞的最大时间(Tick Count)。 如果被设为0,无法读出数据时函数会立刻返回; 如果被设为portMAX_DELAY,则会一直阻塞直到有数据可写
返回值 pdPASS:从队列读出数据入 errQUEUE_EMPTY:读取失败,因为队列空了

写队列

可以把数据写到队列头部,也可以写到尾部,这些函数有两个版本:在任务中使用、在ISR中使用。函数原型如下:

/* 等同于xQueueSendToBack* 往队列尾部写入数据,如果没有空间,阻塞时间为xTicksToWait*/
BaseType_t xQueueSend(QueueHandle_t    xQueue,const void       *pvItemToQueue,TickType_t       xTicksToWait);/* * 往队列尾部写入数据,如果没有空间,阻塞时间为xTicksToWait*/
BaseType_t xQueueSendToBack(QueueHandle_t    xQueue,const void       *pvItemToQueue,TickType_t       xTicksToWait);/* * 往队列尾部写入数据,此函数可以在中断函数中使用,不可阻塞*/
BaseType_t xQueueSendToBackFromISR(QueueHandle_t xQueue,const void *pvItemToQueue,BaseType_t *pxHigherPriorityTaskWoken);/* * 往队列头部写入数据,如果没有空间,阻塞时间为xTicksToWait*/
BaseType_t xQueueSendToFront(QueueHandle_t    xQueue,const void       *pvItemToQueue,TickType_t       xTicksToWait);/* * 往队列头部写入数据,此函数可以在中断函数中使用,不可阻塞*/
BaseType_t xQueueSendToFrontFromISR(QueueHandle_t xQueue,const void *pvItemToQueue,BaseType_t *pxHigherPriorityTaskWoken);
参数说明
xQueue 队列句柄,要写哪个队列
pvItemToQueue 数据指针,这个数据的值会被复制进队列, 复制多大的数据?在创建队列时已经指定了数据大小
xTicksToWait 如果队列满则无法写入新数据,可以让任务进入阻塞状态, xTicksToWait表示阻塞的最大时间(Tick Count)。 如果被设为0,无法写入数据时函数会立刻返回; 如果被设为portMAX_DELAY,则会一直阻塞直到有空间可写
返回值 pdPASS:数据成功写入了队列 errQUEUE_FULL:写入失败,因为队列满了。

 编码实例1

改造红外任务

之前:game1任务->挡球板任务->读取缓冲区,写缓冲区由红外中断完成。这种方式没有数据时会循环读取,导致很耗费cpu资源

改造:game1任务->挡球板任务->读队列,写队列在红外中断中完成。

编码实例2

使用队列实现多任务输入

系统框图

红外中断接收到按键值之后,会把按键值转换为游戏控制的键值写入挡球板队列。编码器中断接收到编码器旋转后,旋转数据写入编码器队列,编码器任务读到数据,进行数据转换(编码器数据转换为游戏数据),并把数据写入挡球板队列。

 使用队列集改造系统结构

红外遥控器、旋转编码器,它们的驱动程序应该专注于“产生硬件数据”,不应该跟“业务有任何联系”。比如:红外遥控器驱动程序里,它只应该把键值记录下来、写入某个队列,它不应该把键值转换为游戏的控制键。在红外遥控器的驱动程序里,不应该有游戏相关的代码,这样,切换使用场景时,这个驱动程序还可以继续使用。把红外遥控器的按键转换为游戏的控制键,应该在红外任务里实现。

但是,如果使用编码器的模式,也就是每个硬件中断都创建一个对应的任务,那么有多个硬件设备时,会很消耗CPU资源。

因此我们引入队列集这一概念,它可以读取多个队列。引入队列集之后,我们就可以只创建一个“inputTask”,来代替红外任务、编码器任务等多个硬件任务。在“inputTask”中,它读取各个设备的队列,得到数据后再分别转换为游戏的控制键。

队列集的本质也是队列,只不过里面存放的是“队列句柄”。使用过程如下:

    • 创建队列A,它的长度是n1
    • 创建队列B,它的长度是n2
    • 创建队列集S,它的长度是“n1+n2”
    • 把队列A、B加入队列集S
    • 这样,写队列A的时候,会顺便把队列A的句柄写入队列集S
    • 这样,写队列B的时候,会顺便把队列B的句柄写入队列集S
    • InputTask先读取队列集S,它的返回值是一个队列句柄,这样就可以知道哪个队列有有数据了;然后InputTask再读取这个队列句柄得到数据。

创建队列集

QueueSetHandle_t xQueueCreateSet( const UBaseType_t uxEventQueueLength )
参数说明
uxQueueLength 队列集长度,最多能存放多少个数据(队列句柄)
返回值 非0:成功,返回句柄,以后使用句柄来操作队列NULL:失败,因为内存不足

把队列放入队列集

BaseType_t xQueueAddToSet( QueueSetMemberHandle_t xQueueOrSemaphore,QueueSetHandle_t xQueueSet );
参数说明
xQueueOrSemaphore 队列句柄,这个队列要加入队列集
xQueueSet 队列集句柄
返回值 pdTRUE:成功pdFALSE:失败

读取队列集

QueueSetMemberHandle_t xQueueSelectFromSet( QueueSetHandle_t xQueueSet,TickType_t const xTicksToWait );
参数说明
xQueueSet 队列集句柄
xTicksToWait 如果队列集空则无法读出数据,可以让任务进入阻塞状态,xTicksToWait表示阻塞的最大时间(Tick Count)。如果被设为0,无法读出数据时函数会立刻返回;如果被设为portMAX_DELAY,则会一直阻塞直到有数据可写
返回值 NULL:失败,队列句柄:成功

改善后的系统框架

 写队列的同时也会写队列集,Input任务会读取队列集得到队列句柄,然后根据句柄进行后续的数据处理,处理完毕后写入挡球板队列。

赛车游戏

红外遥控器,用不同的按键控制三辆汽车

现在有三辆汽车,创建三个汽车任务。在红外中断中将键值写队列,汽车任务要来读队列。因为是三个任务,所以要写三个队列,在这里为了代码的可移植性,我们使用一个队列注册函数以及一个队列分发函数。

void RegisterQueueHandle(QueueHandle_t queueHandle)
{if (g_queue_cnt < 10){g_xQueues[g_queue_cnt] = queueHandle;g_queue_cnt++;}
}
static void DispatchKey(struct ir_data *pidata)
{int i;for (i = 0; i < g_queue_cnt; i++){xQueueSendFromISR(g_xQueues[i], pidata, NULL);}
}

在开始时每个任务调用任务函数时,创建自己的队列,并注册队列,然后循环读取队列,读不到便阻塞。当有红外中断写队列后,阻塞被唤醒。

系统架构

 信号量和互斥量

队列用于代替缓冲区,在任务之间或任务与中断之间传递数据。但有时我们只需要传递一个状态,这个状态值需要用一个数值表示。

信号量本质上起始就是队列,只不过不进行具体数据的传输。

队列:                                                                      信号量:

写:send(①拷贝数据②cnt++③唤醒)                 写:give(①cnt++②唤醒)

读:receive(①拷贝数据②cnt--③唤醒)               读:take(①cnt--)

 

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

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

相关文章

Windows 自动色彩管理(ACM)

在一些笔记本上Win11可以看到设置里有“自动管理应用的颜色”选项,有些笔记上没有。这里讲下“自动管理应用的颜色”的显示规则 看华为MetaBook E设置界面显示:“自动管理应用的颜色”与“颜色自适应”是一个功能? 不是,颜色自适应是要依赖环境颜色传感器来实现的,有环境颜…

闲话 11.20

杂题乱写 11.2010 days left. 不说闲话,捡重点说。P4113 [HEOI2012] 采花 hh 的项链加强版。 首先考虑莫队,轻松写,轻松 133pts,轻松过不了后两个 hack,考虑优化。 既然是加强版,那么就考虑沿用之前的思路。记录上次出现某个数的位置和上上次出现某个数的位置,离线之后将…

CTFshow渗透知识点

1.robot.txtRobots是一个协议,全称为“网络爬虫协议”,也称爬虫协议、机器人协议等。网站通过Robots协议告诉搜索引擎哪些页面可以访问,哪些不可以访问。 使用方法: 在浏览器的网址搜索框中,输入根域名,再输入/robots.txt即可 博客:Robots.txt在渗透测试中的利用_robots.…

Goby 漏洞发布|超高危!Palo-alto-panos createRemoteAppwebSession.php 命令执行漏洞CVE-2024-0012 CVE-2024-947

漏洞名称:Palo-alto-panos createRemoteAppwebSession.php 命令执行漏洞CVE-2024-0012 CVE-2024-947 English Name:Palo-alto-panos /php/utils/createRemoteAppwebSession.php Command Execution Vulnerability CVE-2024-0012 CVE-2024-9474 CVSS core: 9.5 漏洞描述: Palo…

一个.NET开源、快速、功能丰富的跨平台阅读服务器

前言 今天大姚给大家分享一个基于.NET开源的快速、功能丰富的跨平台阅读服务器,它的设计初衷是提供一个全面的解决方案,满足用户的所有阅读需求。用户可以设置自己的服务器,并与朋友和家人分享阅读收藏:Kavita。 支持格式书籍:epub, pdf。 漫画/网络漫画/漫画书:cbr, cbz…

人工智能之机器学习线代基础——矩阵分类

1. 按维度和大小方阵(Square Matrix):行数和列数相等的矩阵。列矩阵(Column Matrix):只有一列的矩阵。行矩阵(Row Matrix):只有一行的矩阵。零矩阵(Zero Matrix):所有元素均为 0。单位矩阵(Identity Matrix):对角线为 1,其他元素为 0 的方阵。对角矩阵(Diagon…

Linux 命令之 tar

目录1 tar 命令介绍2 压缩与解压缩2.1 压缩2.2 解压4 高级用法4.1 排除目录4.2 显示进度4.2.1 脚本解压缩4.2.2 命令解压缩4.2.3 压缩进度 1 tar 命令介绍常见的压缩包有 .tar.gz、.tar.xz、.tar.bz2,以及 .rar、.zip、.7z 等压缩包。常见的 tar 选项:- 1. 文件操作选项:-c,…

2024年11月最新版Adobe PhotoShop(26.0)中文版下载

Adobe PhotoShop 是一款功能强大的应用程序,已被各种公司、专业艺术家、设计和创作者广泛使用。该程序允许您创建、编辑和合成多层、蒙版和多种颜色模型(包括 RGB、专色、CMYK 等)的光栅图像。点击跳转下载页面 Adobe PhotoShop 是一款功能强大的应用程序,已被各种公司、专…

SpringBoot如何集成mybatis-plus

前言 大家好,我是小徐啊。我们在使用SpringBoot的时候,毫无疑问,一般是和mybatis集成的,而mybatis-plus作为mybatis的进阶,又是必须要集成的。今天,小徐就来介绍下如何在SpringBoot中集成mybatis-plus。 如何集成 首先,讲一下如何在配置文件中配置mybatis-plus的配置。主…

ubuntu安装docker+后端发布

一:更新软件包索引,并且安装必要的依赖软件 1.sudo apt-get update #更新软件包索引 2.sudo apt-get install apt-transport-https ca-certificates curl gnupg-agent software-properties-common lsb-release #安装apt依赖包,用于通过HTTPS来获取Docker 仓库 二:添加 D…

IDEA如何找到在IDEA中下载jdk

前言 大家好,我是小徐啊。在使用IDEA开发java应用的时候,都是需要配置好jdk的环境的。当然,如果我们一开始,先安装好了jdk,那就不需要安装了。不然的话,我们也可以在IDEA中便捷的安装jdk。今天,小徐就来教大家如何在IDEA中安装jdk。 如何下载jdk 首先,点击下文件,项目…

第十二课 接口文档和编写接口测试用例(12.1)

一、熟悉接口文档和分析接口 1、发送接口文档 2、分析接口文档 3、了解需要测试接口,分析需求文档接口请求参数:接口返回参数:成功接口返回参数:失败================================================================================ 整理接口:(自己项目有哪些借款) …