【RT-Thread基础教程】消息队列的使用

文章目录

  • 前言
  • 一、消息队列是什么
  • 二、传输数据的两种方法
  • 三、 消息队列的阻塞访问
  • 四、消息队列的使用
    • 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 发送消息

  1. 等待方式发送消息
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来无限等待

  1. 发送消息
    我们可以使用下面这个函数来发送不等待的消息
rt_err_t rt_mq_send (rt_mq_t mq, void* buffer, rt_size_t size);
  1. 紧急消息的发送
    使用这个函数会把发送的消息插到队列的最前面,无视其他消息,这样才是紧急的
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 中使用消息队列提供指导,使您更加熟练地运用这一重要的系统资源。

  • 61
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

人才程序员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值