SylixOS---消息队列

消息队列

问题:
有两个线程(线程 A、线程 B,线程 A 的优先级较线程 B 的高)和一个变量 V,线程 A 需要写变量 V,线程 B 需要读变量 V。
我们假设只有在变量 V 的值改变时,线程 B 才需要读变量 V,在变量 V 的值不变时,线程 B 需要阻塞。
如果我们继续使用条件变量进行线程间通信,在线程 A 快速频繁地修改变量 V 的值时,可能会造成线程 B 丢失一部分对变量 V 值改变的响应——应该被读出的旧值已经被新值所覆盖。

解答:
消息队列是一个可以存放多则消息的 FIFO(先入先出)队列。如果我们改用消息队列
作为线程 A、B 间的通信手段,线程 A 将变量 V 修改后的值作为一则消息存入消息队列,
线程 B 只需要从消息队列读出消息(即变量 V 修改后的值),那么在消息队列满前就不会出现线程 B 丢失一部分对变量 V 值改变的响应。所以说消息队列是属于异步操作
在这里插入图片描述

1.什么是消息队列

消息队列是一种常用于任务间通信的数据结构,消息队列可以在线程与线程间中断和线程间传送信息,实现了线程接收来自其他线程或中断的不固定长度的消息。

消息队列是消息的链表,存放在内核中并由消息队列标识符(消息队列ID)表示。
在这里插入图片描述

2.运作机制

2.1 SylixOS中消息队列一般流程

一个 SylixOS 消息队列必须要调用 Lw_MsgQueue_Create 函数创建之后才能使用,如果创建成功,Lw_MsgQueue_Create 函数将返回一个消息队列的句柄。
线程如果需要接收消息,可以调用 Lw_MsgQueue_Receive 函数,中断服务程序不能调用 Lw_MsgQueue_Receive 函数接收消息,因为 Lw_MsgQueue_Receive 函数在消息队列为空时会阻塞当前线程。
中断服务程序可以使用 Lw_MsgQueue_TryReceive 函数尝试接收消息,Lw_MsgQueue_TryReceive 函数在消息队列为空队列时将立即返回,不会阻塞当前线程。
发送消息可以调用 Lw_MsgQueue_Send 函数。
当一个消息队列使用完毕后(并确保以后也不再使用),应该调用 Lw_MsgQueue_Delete
函数将其删除,SylixOS 会回收该消息队列占用的内核资源。
在这里插入图片描述

2.2 创建

SylixOS通过Lw_MsgQueue_Create创建消息队列。

在这里插入图片描述
在这里插入图片描述

消息队列选项
在这里插入图片描述

消息队列会被很多线程访问时,线程需要一个队列进行排队访问,当uloption选择LW_OPTION_WAIT_PRIORITY时,等待的线程会以优先级排序,如果是LW_OPTION_WAIT_FIFO时,则是会通过FIFO队列等待读取消息。

消息队列创建时,会先从缓存池中获取一个空闲的消息队列控制块,在给消息队列分配一块内存空间,内存的大小 = 消息队列控制块大小 + 单个消息空间大小 x 消息队列长度
在分配内存之后,系统会初始化消息队列,此时消息队列为空。
在这里插入图片描述

每个消息空间可存放不大于消息大小的任意类型数据,所有消息队列中的消息空间总数就是消息队列的长度,长度可在消息队列创建时指定。

每个消息队列都与消息空间在同一段连续的内存空间,创建成功后,这些内存就被占用
只有调用函数Lw_MsgQueue_Delete删除了消息队列后,这段内存才会被释放,创建成功,已经分配给每个消息空间与消息队列的容量无法更改。
在这里插入图片描述

系统删除队列时会先将线程等待队列中无论是等待优先级排序还是fifo排序的线程全部进行激活,之后将控制块交还给缓存池。

任务或中断都可以给队列发送消息,如果队列未满或者使用覆盖入队,消息将会被拷贝(赋值)到队尾,否则任务会根据设置的阻塞时间进行阻塞。

线程可以从队列读取消息,消息从队头开始被取出,如果队列为空则根据设置的阻塞时间进行阻塞。
在这里插入图片描述

2.3发送普通消息:

通过图片可对消息队列有个具体认知。
在这里插入图片描述

—》该队列可以存放多个数据单元,每个数据单元大小不一但是有最大长度;
—》task1给task2传递数据事先将消息发送至消息队列,task2从队列中获取数据;
—》fifo方式存取;
存取过程会出现两种情况:
a、task1存放数据速度大于task2 速度,会出现队列放满的情况,SylixOS中的Lw_MsgQueue_Send2已经实现超时等待功能,用户可以设置超时等待时间直到超时时间溢出或者是队列有空;
这意味着,当发送的消息队列满时,发送消息将等待ulTimeout 时间,如果超时时间到时消息队列仍然处于满状态消息将被丢弃,否则消息被成功发送。
在这里插入图片描述

b、队列会满当然也会空,当队列为空,读取数据依然可以通过函数Lw_MsgQueue_ReceiveEx设置超时等待,直到超时或者是队列有数据。
当队列中不存在消息时,线程将被阻塞,如果设置了 ulTimeout 的超时值为LW_OPTION_WAIT_INFINITE,线程将永远阻塞直到消息到来;如果 ulTimeout 的超时值不为 LW_OPTION_WAIT_INFINITE,线程将在指定的时间超时后自动唤醒线程。

2.4发送紧急消息:

消息队列通常作为先入先出(FIFO)实现, 这意味着收到的第一个消息将是从队列中提取的第一个消息。 然而, 有些内核允许发送被认为比其他内核更重要的消息, 从而排在队列的首位。 换句话说, 在"先入先出"顺序中, 使该消息成为任务提取的第一条信息。

发送紧急消息的过程与发送普通消息几乎一样,不同的是发送紧急消息时,消息的位置是消息队列队列头,而非是队尾(消息先出)。这样接收者可优先接收紧急消息,可及时进行消息处理。
在这里插入图片描述

消息选项
在这里插入图片描述

如果使用 LW_OPTION_URGENT 选项,那么该消息将被插入到消息队列的首部。如果
使用LW_OPTION_BROADCAST 选项,那么该消息将被传递到每一个等待该消息队列的线程。

3.阻塞机制

使用的消息队列一般不是属于某个任务的队列,创建的队列是共用的,每个任务都可对队列进行读写操作,为了保护每个任务对其进行读写操作的过程,必须要有阻塞机制。
某个任务对消息队列读写操作时,必须保证任务能完成读写操作,不受其他任务的干扰
保护任务对消息队列的读写操作的过程,这种机制称为阻塞机制。

任务A读队列,当消息队列没有消息,则A有三种可能:

1、任务A不设置超时等待,就会继续执行,不会陷入阻塞;
2、任务A阻塞,等待消息,可以通过ulTimeout的值指定等待时长,超时则变回就绪态,发送任务返回错误码(有消息就变回就绪态,准备继续执行);
3、任务A的ulTimeout 的超时值为LW_OPTION_WAIT_INFINITE,线程将永远阻塞直到消息到来,再完成消息队列的读取。

需要注意的是,中断中发送消息是不支持消息等待的,也是不允许阻塞的,这会影响系统的实时性,中断接收可以通过Lw_MsgQueue_TryReceive判断,如果队列为空则立即退出不进行超时等待。

多个任务因为一个消息队列阻塞,阻塞任务会按照任务的优先级进行排序,优先级高的优先访问队列,或者进行FIFO队列等待,先进先获取对消息队列的信息获取。

4.优势

消息队列使得我们的软件更容易按功能模块划分和实现,不同功能模块使用不同线程实现,功能模块之间使用消息队列进行通信解耦合而不是定义调用接口。

比如ADC线程读取 ADC 转换后的结果,将结果存入消息队列,UI 线程从消息队列取出结果将其显示到屏幕上;按键线程读取用户按下的按键,将键值存入消息队列,UI 线程取出键值切换显示页面……,这样只有UI 线程能操作显示界面,避免了显示错误的发生。

5.缺点

在实际应用中,一般来说只有一个任务从消息队列接收消息,有一个或多个任务发送消息,即这个消息队列有多个生产者,而只有一个消费者。

消息队列时单向的,对于需要进行双向通信的两个任务,必须使用两个消息队列。消息队列非常适合于Client-Server结构的任务之间的通信。

消息队列是一种代价比较高的一种通信机制,因此在使用时应该使消息的长度尽量短,而且应避免在需要十分频繁通信的场合使用消息队列。

另外消息队列中的消息是排队的,即使是完全相同的消息,后面的消息也不会覆盖前面的消息,重复消息越多越可能造成资源上的浪费。

6.其他操作

6.1队列清除:

消息队列的清除意味着队列中的所有消息将被删除(消息丢弃,队列仍然有效),企图从中接收消息不会得到预期的结果。

6.2释放等待消息队列的所有线程:

Flush 函数将使所有阻塞在指定消息队列上的线程(包括发送和接收线程)就绪,这样避免了线程长时间阻塞的状态。
FlushSend 函数将使所有阻塞在指定消息队列上的发送线程就绪,这样避免了发送线程因为长时间发送不出去消息而长时间阻塞的状态。
FlushReceive 函数将使所有阻塞在指定消息队列上的接收线程就绪,这样避免了接收线程因为长时间接收不到消息而长时间阻塞的状态。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值