文章目录
- 前言
- 一、消息队列是什么
- 二、传输数据的两种方法
- 三、 消息队列的阻塞访问
- 四、消息队列的使用
- 4.1 创建队列
- 4.2 删除/脱离
- 4.3 发送消息
- 4.4 读取消息
- 五、示例代码
- 总结
前言
消息队列是实时操作系统 RT-Thread 中一种常用的进程间通信(IPC)机制,用于在不同线程之间传递消息。在嵌入式系统中,多个线程可能同时运行,而它们之间需要进行信息的传递和共享。消息队列为线程之间提供了一种高效、灵活的通信方式,使得系统的各个组件能够协同工作。
本文将介绍在 RT-Thread 中如何使用消息队列,包括创建消息队列、发送和接收消息等基本操作。通过学习消息队列的使用,您将能够更好地设计和实现多线程的嵌入式应用程序,提高系统的可维护性和可扩展性。
一、消息队列是什么
当你玩游戏时,有时候需要和队友交流,比如告诉他们你的位置、需求或计划。RT-Thread 的消息队列就像是一个队伍中的通信系统,让不同的队友(线程)之间可以传递信息。你可以把消息队列想象成一个传纸条的系统,你写下你想说的话,放到队列中,其他队友可以取出来看到你的信息,然后回复或者采取行动。这样,队伍中的成员就可以有效地沟通和协作,完成各自的任务,而不需要直接面对面交流。
如果你学习过FreeRTOS,他其实就是FreeRTOS中的队列Queue。
二、传输数据的两种方法
使用消息队列传输数据时有两种方法:
- 拷贝:把数据、把变量的值复制进消息队列里
- 引用:把数据、把变量的地址复制进消息队列里
RT-Thread 使用拷贝值的方法,这更简单:
局部变量的值可以发送到消息队列中,后续即使函数退出、局部变量被回
收,也不会影响消息队列中的数据
- 无需分配 buffer 来保存数据,消息队列中有 buffer
- 局部变量可以马上再次使用
- 发送线程、接收线程解耦:接收线程不需要知道这数据是谁的、也不需要
发送线程来释放数据 - 如果数据实在太大,你还是可以使用消息队列传输它的地址
- 消息队列的空间有 RT-Thread 内核分配,无需线程操心
三、 消息队列的阻塞访问
只要知道消息队列的句柄,谁都可以读、写该消息队列。线程、ISR 都可读、写消息队列。可以多个线程读写消息队列。线程读写消息队列时,如果读写不成功,可以即刻返回错误,也可以阻塞。阻塞时可以指定超时时间。口语化地说,就是可以定个闹钟:如果能读写了就马上进入就绪态,否则就阻塞直到超时。
比如某个线程读消息队列时:
如果消息队列中有可用的消息,即刻得到消息并返回
如果消息队列中没有可用的消息,线程有两种选择:即刻放回一个错误值,或者阻塞一段时间
如果线程阻塞,它何时被唤醒?
在指定的时间内,别的线程或者中断服务程序写了队列,会把它唤醒
否则,指定的时间到达后超时返回错误。既然读写消息队列的线程个数没有限制:
多个线程都想写队列,但是队列已经满了,这些线程可以进入阻塞状态:它们都在等待队列有空间
那么:队列有空间时,把那个线程唤醒?
多个线程都想读队列,但是队列已经空了,这些线程可以进入阻塞状态:它们都在等待队列有数据
那么:队列有数据时,把那个线程唤醒?
唤醒谁?有两种方法。创建队列时,可以指定一个参数 flag
RT_IPC_FLAG_PRIO:表示唤醒优先级最高的等待线程
RT_IPC_FLAG_FIFO:表示唤醒等待时间最长的等待线程
四、消息队列的使用
4.1 创建队列
我们可以使用下面这个函数创建动态的消息队列:
rt_mq_t rt_mq_create(const char *name,
rt_size_t msg_size,
rt_size_t max_msgs,
rt_uint8_t flag)
参数1为消息队列的名称,参数2为每一个消息的大小,参数3为最大有多少个消息,参数4为唤醒方式:
RT_IPC_FLAG_PRIO:表示唤醒优先级最高的等待线程
RT_IPC_FLAG_FIFO:表示唤醒等待时间最长的等待线程
我们还可以创建静态的消息队列:
rt_err_t rt_mq_init(rt_mq_t mq,
const char *name,
void *msgpool,
rt_size_t msg_size,
rt_size_t pool_size,
rt_uint8_t flag)
参数1为存储消息队列的变量。参数3为存储消息的指针。参数5为存储消息的内存大小
4.2 删除/脱离
删除它:rt_mq_delete(),只能删除使用 rt_mq_create()创建的队列
脱离它:rt_mq_detach(),只能脱离使用 rt_mq_init()初始化的队列
删除消息队列时,如果有线程在等待该队列,则内核会先唤醒这些线程(线程返回值
是 RT_ERROR),然后再释放消息队列使用的内存,最后删除消息队列对象。
4.3 发送消息
- 等待方式发送消息
rt_err_t rt_mq_send_wait(rt_mq_t mq,
const void *buffer,
rt_size_t size,
rt_int32_t timeout)
他发送一个消息,如果消息队列满了,则等待timeout个tick。
参数1为你要给那个消息队列发送消息,参数2为发送的消息的buf,参数3为发送buf的大小
void thread2t(void *param)
{
rt_uint32_t i = 0;
while(1)
{
rt_mq_send_wait(messageQueue,&i,1,RT_WAITING_FOREVER);
i++;
}
}
我们可以使用RT_WAITING_FOREVER
来无限等待
- 发送消息
我们可以使用下面这个函数来发送不等待的消息
rt_err_t rt_mq_send (rt_mq_t mq, void* buffer, rt_size_t size);
- 紧急消息的发送
使用这个函数会把发送的消息插到队列的最前面,无视其他消息,这样才是紧急的
rt_err_t rt_mq_urgent(rt_mq_t mq, void* buffer, rt_size_t size);
4.4 读取消息
我们可以使用下面这个函数去读取一个消息队列:
rt_err_t rt_mq_recv (rt_mq_t mq, void* buffer,
rt_size_t size, rt_int32_t timeout);
参数1为读取哪个队列,参数2为读取存到哪,参数3为buf的大小,参数4为读取的等待时间
五、示例代码
/*
* Copyright (c) 2006-2024, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2024-02-22 RT-Thread first version
*/
#include <rtthread.h>
#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
rt_uint32_t test_stack[512];
rt_mq_t messageQueue;
void thread1t(void *param)
{
int i = -1;
while(1)
{
rt_mq_recv(messageQueue,&i,4,RT_WAITING_FOREVER);
rt_kprintf("Recv data:%d\n",i);
rt_thread_mdelay(1000);
}
}
void thread2t(void *param)
{
rt_uint32_t i = 0;
while(1)
{
rt_mq_send_wait(messageQueue,&i,4,RT_WAITING_FOREVER);
i++;
}
}
int main(void)
{
messageQueue = rt_mq_create("mqFirst", 4, 10, RT_IPC_FLAG_FIFO);
rt_thread_t thread1 = rt_thread_create("thread", thread1t, NULL, 1000, 1, 10);
if (thread1 != RT_NULL)
{
if (rt_thread_startup(thread1) == RT_EOK)
{
rt_kprintf("Thread1 started successfully.\n");
}
else
{
rt_kprintf("Failed to start Thread1.\n");
}
}
else
{
rt_kprintf("Failed to create Thread1.\n");
}
rt_thread_t thread2 = rt_thread_create("thread2", thread2t, NULL, 1000, 1, 10);
if (thread2 != RT_NULL)
{
if (rt_thread_startup(thread2) == RT_EOK)
{
rt_kprintf("thread2 started successfully.\n");
}
else
{
rt_kprintf("Failed to start thread2.\n");
}
}
else
{
rt_kprintf("Failed to create thread2.\n");
}
return 0;
}
总结
消息队列是 RT-Thread 中重要的进程间通信机制之一。通过消息队列,不同线程能够以异步的方式进行信息的传递,实现了松耦合的线程通信。在实际应用中,消息队列常用于事件处理、数据传输等场景,为多线程协同工作提供了便利。
在使用 RT-Thread 的消息队列时,首先需要创建消息队列对象,然后通过发送和接收消息的操作实现线程之间的通信。通过合理的设计和使用消息队列,可以提高系统的响应速度和并发处理能力,确保嵌入式系统的稳定性和可靠性。希望本文能够为您在 RT-Thread 中使用消息队列提供指导,使您更加熟练地运用这一重要的系统资源。