核心差异:全局变量是**“裸奔”的数据共享,甚至可能读到一半被改写;消息队列(Queue)是“线程安全”**的管道,自带同步与缓冲功能。
1. 全局变量的隐形杀手:脏读 (Dirty Read)
假设我们有一个结构体,包含两个变量,由 任务A(采集) 写入,任务B(显示) 读取。
struct SensorData {
int x;
int y;
} g_data;
// 任务A:采集传感器坐标
void Task_Sensor() {
g_data.x = 100;
// <--- 就在这一瞬间,高优先级的任务B抢占了! --->
g_data.y = 200;
}
// 任务B:显示坐标
void Task_Display() {
printf("X=%d, Y=%d", g_data.x, g_data.y);
}
事故推演:
-
旧数据是
{x=0, y=0}。 -
任务A 刚把
x更新为100。 -
任务B 抢占执行,读取数据。它读到了 新 的
x=100和 旧 的y=0。 -
结果:任务B 拿到了一个现实中从未存在过的坐标
{100, 0}。
这就是 脏读。数据的一致性被破坏了。在裸机里,我们靠关中断来保护;而在 RTOS 里,我们有了更高级的工具——队列。
2. 消息队列:不仅仅是数组
消息队列 (Queue) 是 RTOS 中最基本、最重要的通讯机制。你可以把它想象成一个传送带。
-
生产者 (Producer):把数据放入传送带入口。
-
消费者 (Consumer):从传送带出口拿走数据。
为什么它比全局变量好?
-
原子性 (Atomicity):RTOS 保证数据入队和出队的操作是完整的。任务B 永远不会从队列里读到“半个结构体”。
-
数据拷贝 (Copy by Value):当你发送数据时,RTOS 通常会把数据内容复制到队列的内存区中。就算任务A 里的局部变量销毁了,队列里的数据依然有效。
-
缓冲 (Buffering):如果任务A 生产得快,任务B 消费得慢,队列可以暂存这些数据(比如深度为 10),防止数据丢失。
代码形态:
// 定义一个队列句柄
QueueHandle_t xQueue;
// 任务A (发送)
void Task_Sender(void *pvParam) {
int value = 0;
while(1) {
value++;
// 把 value 的值复制进队列
xQueueSend(xQueue, &value, 0);
vTaskDelay(100);
}
}
// 任务B (接收)
void Task_Receiver(void *pvParam) {
int buffer;
while(1) {
// 从队列读数据。
// 核心魔法:如果队列是空的,任务B会自动阻塞 (睡觉),不占CPU!
// 一旦有数据入队,任务B 瞬间醒来处理。
xQueueReceive(xQueue, &buffer, portMAX_DELAY);
printf("Received: %d", buffer);
}
}
3. 自带的“流量控制”功能
除了传递数据,队列还有一个裸机全局变量无法比拟的功能:阻塞同步。
场景:串口接收任务(快)往队列里塞数据,SD卡写入任务(慢)从队列取数据写文件。
-
如果队列满了:
-
裸机数组:直接覆盖旧数据(丢包),或者指针乱飞导致溢出。
-
RTOS队列:
xQueueSend可以选择阻塞。如果队列满了,发送任务会进入睡眠,暂停生产,等待接收任务腾出空间。这天然实现了一个流量控制阀,防止下游任务被冲垮。
-
-
如果队列空了:
-
裸机变量:接收者必须不断轮询检查
if (flag == 1),浪费 CPU。 -
RTOS队列:接收任务阻塞睡眠,完全不耗电。
-
4. 什么时候不用队列?
队列虽然好,但也不是万能的。 由于涉及到 内存拷贝 和 内核调度,队列的操作比直接读写全局变量要慢得多(微秒级 vs 纳秒级)。
-
大块数据传输(如摄像头图像):不要把几百 KB 的图片直接塞进队列!这会把内存撑爆且拷贝太慢。
-
正确做法:把图片的指针 (Address) 塞进队列。消费者拿到钥匙(指针)后,直接去仓库(内存)取货。
-
-
极高频的数据(如 20kHz 电机环路):队列开销太大。这种场景下,依然会小心翼翼地使用全局变量配合互斥量或关中断。
关键点总结
-
全局变量:快,但危险。容易发生脏读,缺乏同步机制。
-
消息队列:安全,解耦。实现了“生产者-消费者”模型,自带缓冲和阻塞唤醒机制。
-
原则:任务间传命令、传状态、传少量数据,首选队列。
/******************************************************
* 本文为作者《嵌入式开发基础与工程实践》系列文章之一。
* 关注即可订阅后续内容更新,采用异步推送机制,无需主动轮询。
* 转发本文可视为一次网络广播,有助于更多节点接收该信息。
******************************************************/
29

被折叠的 条评论
为什么被折叠?



