【RT-Thread】消息队列

RT-Thread 消息队列机制


1. 消息队列(Message Queue)基本概念

  • 消息队列:线程间通信的一种机制,用于异步传递固定大小的数据块
  • 典型特点:发送方和接收方松耦合,不需要直接同步等待对方。

在RT-Thread中,消息队列适用于频繁产生数据处理时间较长的应用场景,例如:传感器采样、任务通知。


2. 消息队列的核心结构

  • 消息池(pool):固定数量的消息块组成的循环缓冲区,用来存放具体消息内容。

  • 链表1 - 空闲消息块链表

    • 管理当前未使用的消息块。
    • 发送消息时,从空闲链表取出一个块,写入数据。
  • 链表2 - 已用消息块链表

    • 管理已经发送但尚未被接收的消息块。
    • 接收线程从此链表中读取消息。

简化示意:

[ 空闲消息块链表 ] ← 发送 → [ 已用消息块链表 ] ← 接收

3. 消息队列操作详解

3.1 创建消息队列

rt_mq_t mq = rt_mq_create("mq1",     // 名称
                           32,        // 每条消息32字节
                           10,        // 最多10条消息
                           RT_IPC_FLAG_FIFO); // 先进先出调度
  • 消息池大小 = 32字节 × 10条 = 320字节。
  • RT_IPC_FLAG_FIFO:发送顺序 = 接收顺序(也可选优先级调度)。

3.2 发送消息

char data[32] = "hello";
rt_mq_send(mq, data, sizeof(data));
  • 内部流程:
    • 尝试从空闲链表取出一个空块。
    • data 复制到空块中。
    • 将消息块挂入已用链表尾部。
    • 如果有线程在等待接收,唤醒一个接收线程。

关于"76次直接写入"

在发送消息时,RT-Thread内部会最多尝试76次寻找空闲消息块:

  • 如果找到空块,成功写入消息。
  • 如果循环76次仍然找不到,认为队列已满,发送失败,返回-RT_EFULL

小总结:

项目 描述
76次扫描 最多循环76次找空闲消息块
成功情况 立即找到空块,不需要76次
队列满了 76次后认定失败,返回错误
为什么是76 折中考虑(效率+资源)

3.3 接收消息

char recv_buf[32];
rt_mq_recv(mq, recv_buf, sizeof(recv_buf), RT_WAITING_FOREVER);
  • 内部流程:
    • 检查已用链表是否有消息块。
    • 如果有,从链表头取出一条消息,复制到recv_buf中。
    • 释放消息块,放回空闲链表
    • 如果没有消息,根据超时时间等待。

3.4 删除消息队列

rt_mq_delete(mq);
  • 释放内存资源,清空消息池,删除链表管理。

4. 消息队列内部链表管理流程图

【发送时】
空闲链表取块 → 写数据 → 加入已用链表

【接收时】
已用链表取块 → 读数据 → 放回空闲链表

5. 消息队列中的线程休眠、唤醒与定时器机制

5.1 线程状态变化

  • 发送消息

    • 队列未满 → 直接发送
    • 队列已满 → 发送线程休眠(可设置超时)
  • 接收消息

    • 队列有数据 → 直接接收
    • 队列为空 → 接收线程休眠(可设置超时)

5.2 定时器的引入

  • 如果指定超时时间,休眠时会启动一个内核定时器(rt_timer)。
  • 超时后如果线程未被唤醒,则由定时器回调将线程唤醒并返回超时。

5.3 整体休眠/唤醒流程

发送线程或接收线程休眠时
├── 检查是否设置超时
│   ├── 是:启动定时器
│   └── 否:一直等待
├── 有资源可用时被唤醒(正常)
└── 超时定时器到期被唤醒(异常)

示例代码

发送线程(带超时保护)

void sender_thread(void *parameter)
{
    char msg[] = "sensor data";
    while (1)
    {
        if (rt_mq_send_wait(mq, msg, sizeof(msg), 200) != RT_EOK)
        {
            rt_kprintf("Send timeout or error!\n");
        }
        rt_thread_mdelay(500);
    }
}

接收线程(带超时保护)

void receiver_thread(void *parameter)
{
    char buffer[32];
    while (1)
    {
        if (rt_mq_recv(mq, buffer, sizeof(buffer), 100) == RT_EOK)
        {
            rt_kprintf("Received: %s\n", buffer);
        }
        else
        {
            rt_kprintf("Receive timeout!\n");
        }
    }
}

6. 总结

  • 消息队列提供了线程间异步通信机制。
  • 通过空闲链表已用链表管理消息块。
  • 发送时最多扫描76次寻找空位。
  • 接收时从已用链表取出消息,并归还空闲链表。
  • 涉及到线程休眠/唤醒,必要时还涉及定时器保护超时场景。

RT-Thread的消息队列机制高效而简洁,非常适合需要轻量化、高实时通信的场合。


你可能感兴趣的:(RT-Thread学习笔记,stm32,c语言)