RT-Thread学习笔记(8):消息队列

基本概念

消息队列,是一种常用于线程间通信的数据结构,队列可以在线程与线程间中断和线程间传送信息,实现了线程接收来自其他线程或中断的不固定长度的消息,并根据不同的接口选择传递消息是否存放在线程自己的空间。线程能够从队列里面读取消息,当队列中的消息是空时,挂起读取线程,用户还可以指定挂起的线程时间timeout;当队列中有新消息时,挂起的读取线程被唤醒并处理新消息,消息队列是一种异步的通信方式。

通过消息队列服务,线程或中断服务例程可以将一条或多条消息放入消息队列中。同样,一个或多个线程可以从消息队列中获得消息。当有多个消息发送到消息队列时,通常是将先进入消息队列的消息先传给线程,也就是说,线程先得到的是最先进入消息队列的消息,即先进先出原则(FIFO)。同时RT-Thread中的消息队列支持优先级,也就是说在所有等待消息的线程中优先级最高的会先获得消息。

用户在处理业务时,消息队列提供了异步处理机制,允许将一个消息放入队列,但并不立即处理它,同时队列还能起到缓冲消息作用。

RT-Thread中使用队列数据结构实现线程异步通信工作,具有如下特性:

  • 消息支持先进先出方式排队与优先级排队方式,支持异步读写工作方式。
  • 读队列支持超时机制。
  • 支持发送紧急消息,这里的紧急消息是往队列头发送消息。
  • 可以允许不同长度(不超过队列节点最大值)的任意类型消息。
  • 一个线程能够从任意一个消息队列接收和发送消息。
  • 多个线程能够从同一个消息队列接收和发送消息。
  • 当队列使用结束后,需要通过删除队列操作释放内存函数回收。

消息队列的运作机制

创建消息队列时先创建一个消息队列对象控制块,然后给消息队列分配一块内存空间,组织成空闲消息链表,这块内存的大小等于[消息大小+消息头(用于链表连接)]与消息队列容量的乘积,接着再初始化消息队列,此时消息队列为空。

struct rt_messagequeue
{
    struct rt_ipc_object parent;

    void* msg_pool;                     /* 指向存放消息的缓冲区的指针 */

    rt_uint16_t msg_size;               /* 每个消息的长度 */
    rt_uint16_t max_msgs;               /* 最大能够容纳的消息数 */

    rt_uint16_t entry;                  /* 队列中已有的消息数 */

    void* msg_queue_head;               /* 消息链表头 */
    void* msg_queue_tail;               /* 消息链表尾 */
    void* msg_queue_free;               /* 空闲消息链表 */

    rt_list_t suspend_sender_thread;    /* 发送线程的挂起等待队列 */
};
typedef struct rt_messagequeue* rt_mq_t;

在这里插入图片描述
线程或者中断服务程序都可以给消息队列发送消息。当发送消息时,消息队列对象先从空闲消息链表上取下一个空闲消息块,把线程或者中断服务程序发送的消息内容复制到消息块上,然后把该消息块挂到消息队列的尾部。当且仅当空闲消息链表上有可用的空闲消息块时,发送者才能成功发送消息;当空闲消息链表上无可用消息块,说明消息队列已满,此时,发送消息的的线程或者中断程序会收到一个错误码(-RT_EFULL)。

发送紧急消息的过程与发送消息几乎一样,唯一的不同是,当发送紧急消息时,从空闲消息链表上取下来的消息块不是挂到消息队列的队尾,而是挂到队首,这样,接收者就能够优先接收到紧急消息,从而及时进行消息处理。

读取消息时,根据msg_queue_head找到最先入队列中的消息节点进行读取。根据消息队列控制块中的entry判断队列是否有消息读取,对全部空闲(entry为0)队列进行读消息操作会引起线程挂起。

消息队列的阻塞机制

我们使用的消息队列一般不是属于某个线程的队列,在很多时候,我们创建的队列,是每个线程都可以去对他进行读写操作的,但是为了保护每个线程对它进行读写操作的过程,我们必须要有阻塞机制,在某个线程对它读写操作的时候,必须保证该线程能正常完成读写操作,而不受后来的线程干扰,凡事都有先来后到嘛!

那么,如何实现这个先来后到的机制呢,很简单,因为RT-Thread已经为我们做好了,我们直接使用就好了,每个对消息队列读写的函数,都有这种机制,我称之为阻塞机制。假设有一个线程A对某个队列进行读操作的时候(也就是我们所说的出队),发现它没有消息,那么此时线程A有3个选择:第一个选择,线程A扭头就走,既然队列没有消息,那我也不等了,干其它事情去,这样子线程A不会进入阻塞态;第二个选择,线程A还是在这里等等吧,可能过一会队列就有消息,此时线程A会进入阻塞状态,在等待着消息的道来,而线程A的等待时间就由我们自己定义,比如设置1000个tick的等待,在这1000个tick到来之前线程A都是处于阻塞态,当阻塞的这段时间线程A等到了队列的消息,那么线程A就会从阻塞态变成就绪态,如果此时线程A比当前运行的线程优先级还高,那么,线程A就会得到消息并且运行;假如1000个tick都过去了,队列还没消息,那线程A就不等了,从阻塞态中唤醒,返回一个没等到消息的错误代码,然后继续执行线程A的其他代码;第三个选择,线程A死等,不等到消息就不走了,这样子线程A就会进入阻塞态,直到完成读取队列的消息。

而在发送消息操作的时候,为了保护数据,当且仅当空闲消息链表上有可用的空闲消息块时,发送者才能成功发送消息;当空闲消息链表上无可用消息块,说明消息队列已满,此时,发送消息的的线程或者中断程序会收到一个错误码(-RT_EFULL),发送消息并不带有阻塞机制的,因为发送消息的环境可能是在中断中,不允许有阻塞的情况。

没有消息,不等,直接走
只给你1000个tick的时间
时间到读到数据
时间到没读到数据
死等
读到数据
没读到数据
A
就绪/运行
阻塞
就绪/运行
阻塞
就绪/运行
阻塞

消息队列的应用场景

消息队列可以应用于发送不定长消息的场合,包括线程与线程间的消息交换,以及在中断服务函数中给线程发送消息(中断服务例程不可能接收消息)。

消息队列图示

创建新队列

在这里插入图片描述

A线程写入一个变量

在这里插入图片描述

A线程修改变量

在这里插入图片描述

B线程读取队列

在这里插入图片描述

消息队列控制块

在这里插入图片描述

发送消息
rt_err_t rt_mq_send(rt_mq_t mq, const void *buffer, rt_size_t size)
{
    return rt_mq_send_wait(mq, buffer, size, 0);
}
rt_mq_send_wait
	/* 如果不成功:当前线程挂起, 并放入链表: mq->suspend_sender_thread */
	rt_ipc_list_suspend(&(mq->suspend_sender_thread),thread,mq->parent.parent.flag);
	/* 如果指定了超时时间: 最多等多久
	* 设置、启动线程定时器
	*/
	rt_timer_start(&(thread->thread_timer));

一个线程写队列时,如果队列已经满了,它会被挂起,何时被唤醒?

  • 超时

在这里插入图片描述

  • 别的线程读取队列,此时就会空出来一个空闲消息

在这里插入图片描述

参考

本节笔记参考于:野火-《RT-Thread内核实现与应用开发实战》
韦东山 《RT-Thread完全开发手册之内核机制》
以及RT-Thread官网:消息队列

MySQL是当今世界上最流行的关系型数据库管理系统,在开发者和IT从业人员中很受欢迎。MySQL安装需要一些基本的计算机知识和技能,但只要按照正确的步骤进行,大多数人都能在很短的时间内安装和配置MySQL。下面是基于Windows平台的MySQL安装和配置超详细教程。 1. 下载MySQL安装程序 访问MySQL官方网站,选择相应的平台和版本,下载MySQL安装程序。可以从官方网站得到MySQL二进制安装文件,也可以使用通过包管理器安装MySQL。 2. 运行MySQL Installer程序 双击下载的安装程序,选择“Install MySQL Products”,并接受授权协议。此时,MySQL Installer应该还会提示您安装一些其他必要的工具。 3. 下载MySQL Server 选择“Custom”类型,选择“MySQL Server”并标记为“Will be installed”,同时选择默认选项。然后点击“Next”。 4. 配置MySQL Server 接下来是配置MySQL Server的步骤。这里你需要为MySQL Server设置一个root用户的密码,以及其他一些选项。 5. 启动MySQL Server 在MySQL Installer中找到“Shell”,选择“Launch MySQL Shell”,启动一个MySQL shell。 6. 连接到MySQL服务器 输入以下命令连接到MySQL服务器: mysqlsh --uri=root:@localhost:3306 这里,root:@localhost:3306代表用户名、密码和MySQL服务器的主机名和端口号。 7. 创建MySQL数据库 在MySQL shell中,运行以下命令创建一个名为“mydatabase”的数据库: create database mydatabase; 8. 创建MySQL用户和密码 运行以下命令创建一个名为“myusername”的用户,密码为“mypassword”,并授予对于“mydatabase”数据库的所有访问权限: create user 'myusername'@'localhost' identified by 'mypassword'; grant all privileges on mydatabase.* to 'myusername'@'localhost'; 9. 连接到指定的数据库 运行以下命令连接到“mydatabase”数据库: use mydatabase; 10. 数据库测试 在“mydatabase”数据库中,可以创建表、插入数据以及查询数据来测试MySQL数据库是否运行正常。这里只是简单的测试,可以运行以下命令: create table mytable (id int, name varchar(20)); insert into mytable values (1, 'hello'); select * from mytable; 通过这些步骤,您已经完成了在Windows平台上安装MySQL Server的所有步骤,并成功地创建了MySQL用户和数据库。之后需要进行关于MySQL的配置、网络配置、数据备份、数据恢复等等部署后续事宜。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值