邮箱#
数据竞争与同步问题#
多线程环境下,如果多个线程同时读写⼀个全局变量,⽽没有使⽤锁或同步机制,会导致数据竞争,即多个线程同时操作全局变量,导致结果不确定或数据错误
邮箱/消息队列的优势:#
邮箱和消息队列都是线程安全的通信机制,能够保证消息传递的原⼦性和数据⼀致性。在使⽤这些⼯具时,操作系统会⾃动处理线程间的同步问题,不会发⽣类似的数据竞争。
如果 thread1 先将 global_value 设置为 10,然后 thread2 将其改为 20,⽽ thr ead3 需要读取这个值,那么 thread3
可能读取到的是最新的 20,⽽忽略了中间的10。也就是说,如果通信信息被新值覆盖了,旧值将永远丢失。
邮箱/消息队列的优势:#
邮箱和消息队列可以缓冲多个消息,并且消息不会被覆盖。当多个线程发送消息时,这些消息会被保存在队列中,直到⽬标线程逐⼀处理所有消息。这确保了数据的完整性和顺序性
邮箱#
邮箱在操作系统中类似于⼀个专⻔⽤来传递“邮件”的信箱。线程 1 可以将⼀封邮件(即 4 字节的消息)放⼊邮箱,线程 2 则可以从邮箱中接收到这封邮件。这种机制适合多个线程相互之间传递简单的数据或信息。
邮箱的⼯作机制#
发送邮件:线程将⼀条 4 字节的数据(邮件)发送到邮箱。如果邮箱满了,发送⽅可以选择等待,直到邮箱中有空位。 接收邮件:接收⽅线程从邮箱中读取邮件。如果邮箱为空,接收⽅可以选择等待,直到邮箱中有新邮件。
线程1 发送⼀封邮件(字符 ‘A’)
线程2 从邮箱中接收邮件,并打印出来
/*
* Copyright (c) 2006-2025, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2025-02-06 RT-Thread first version
*/
#include <rtthread.h> // 包含RT-Thread的核心头文件
#define DBG_TAG "main" // 定义日志标签
#define DBG_LVL DBG_LOG // 定义日志级别为普通日志
#include <rtdbg.h> // 包含调试日志的头文件
// 定义邮件框句柄
rt_mailbox_t mb;
// 线程1的入口函数,用于发送邮件
void thread_entry1(void *parameter)
{
char aaa_send = 'A'; // 定义要发送的邮件内容,这里是一个字符 'A'
// 打印日志:线程1发送邮件
rt_kprintf("线程1发送邮件...\n");
// 使用 rt_mb_send 将邮件发送到邮件框
// 参数说明:
// mb: 邮件框句柄
// (rt_uint32_t)aaa_send: 要发送的邮件内容(强制转换为 rt_uint32_t 类型)
rt_mb_send(mb, (rt_uint32_t)aaa_send);
}
// 线程2的入口函数,用于接收邮件
void thread_entry2(void *parameter)
{
char aaa_recv; // 定义用于接收邮件内容的变量
// 打印日志:线程2等待接收邮件
rt_kprintf("线程2等待接收...\n");
// 使用 rt_mb_recv 从邮件框接收邮件
// 参数说明:
// mb: 邮件框句柄
// (rt_ubase_t*)&aaa_recv: 邮件内容的接收地址(强制转换为 rt_ubase_t 类型)
// RT_WAITING_FOREVER: 等待时间,表示无限等待
rt_mb_recv(mb, (rt_ubase_t*)&aaa_recv, RT_WAITING_FOREVER);
// 打印接收到的邮件内容
rt_kprintf("线程2接收邮件:%c\n", aaa_recv);
}
// 主函数
int main(void)
{
// 创建邮件框
// 参数说明:
// "mb": 邮件框名称
// 4: 邮件框的容量(表示可以存储的消息数量)
// RT_IPC_FLAG_PRIO: 设置邮件框的模式为优先级继承,防止优先级倒置
mb = rt_mb_create("mb", 4, RT_IPC_FLAG_PRIO);
// 创建线程1
// 参数说明:
// "thread1": 线程名称
// thread_entry1: 线程入口函数
// RT_NULL: 线程参数
// 1024: 线程栈大小(单位:字节)
// 25: 线程优先级(数值越小,优先级越高)
// 10: 线程时间片
rt_thread_t thread1 = rt_thread_create("thread1", thread_entry1, RT_NULL, 1024, 25, 10);
// 创建线程2
// 参数说明:
// "thread2": 线程名称
// thread_entry2: 线程入口函数
// RT_NULL: 线程参数
// 1024: 线程栈大小(单位:字节)
// 25: 线程优先级(数值越小,优先级越高)
// 10: 线程时间片
rt_thread_t thread2 = rt_thread_create("thread2", thread_entry2, RT_NULL, 1024, 25, 10);
// 如果线程1创建成功,则启动线程1
if (thread1 != RT_NULL)
{
rt_thread_startup(thread1);
}
// 如果线程2创建成功,则启动线程2
if (thread2 != RT_NULL)
{
rt_thread_startup(thread2);
}
// 主函数返回0,表示正常结束
return 0;
}
实现效果:

遇到的问题:
rt_mb_recv(mb,(rt_ubase_t*)&aaa_recv,RT_WAITING_FOREVER);
//需要用(rt_ubase_t*),否则报错
消息队列#
你和朋友在共享⼀个快递柜(消息队列),你们可以向柜⼦⾥放⼊不同⼤⼩的包裹(消息),快递柜负责保管这些包裹。另⼀个朋友则可以从快递柜中取出包裹并处理,⽆论是⼤包裹还是⼩包裹都可以轻松处理。
消息队列的⼯作机制#
发送消息:线程可以将任意⼤⼩的消息发送到消息队列。如果消息队列满了,线程会等待,直到有空间可以存储新消息。
接收消息:线程可以从消息队列中接收消息。如果消息队列为空,线程可以选择等待新的消息到来。
thread_entry1 发送⼀条消息到消息队列
thread_entry2 从消息队列接收并打印消息
/*
* Copyright (c) 2006-2025, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2025-02-06 RT-Thread first version
*/
#include <rtthread.h> // 包含RT-Thread的核心头文件
#define DBG_TAG "main" // 定义日志标签
#define DBG_LVL DBG_LOG // 定义日志级别为普通日志
#include <rtdbg.h> // 包含调试日志的头文件
// 定义消息队列句柄
rt_mq_t mq;
// 线程1的入口函数,用于发送消息
void thread_entry1(void *parameter)
{
char message[] = "Hello,RT-Thread!"; // 定义要发送的消息内容
// 打印日志:线程1发送邮件
rt_kprintf("线程1发送邮件...\n");
// 使用 rt_mq_send 将消息发送到消息队列
// 参数说明:
// mq: 消息队列句柄
// message: 要发送的消息内容
// sizeof(message): 消息的大小(字节)
rt_mq_send(mq, message, sizeof(message));
}
// 线程2的入口函数,用于接收消息
void thread_entry2(void *parameter)
{
char buffer[32]; // 定义用于接收消息内容的缓冲区
// 打印日志:线程2等待接收消息
rt_kprintf("线程2等待接收...\n");
// 使用 rt_mq_recv 从消息队列接收消息
// 参数说明:
// mq: 消息队列句柄
// buffer: 消息内容的接收缓冲区
// sizeof(buffer): 缓冲区的大小(字节)
// RT_WAITING_FOREVER: 等待时间,表示无限等待
rt_mq_recv(mq, buffer, sizeof(buffer), RT_WAITING_FOREVER);
// 打印接收到的消息内容
rt_kprintf("线程2收到消息队列消息:%s\n", buffer);
}
// 主函数
int main(void)
{
// 创建消息队列
// 参数说明:
// "mq": 消息队列名称
// 32: 每条消息的大小(字节)
// 4: 消息队列的容量(表示可以存储的消息数量)
// RT_IPC_FLAG_PRIO: 设置消息队列的模式为优先级继承,防止优先级倒置
mq = rt_mq_create("mq", 32, 4, RT_IPC_FLAG_PRIO);
// 创建线程1
// 参数说明:
// "thread1": 线程名称
// thread_entry1: 线程入口函数
// RT_NULL: 线程参数
// 1024: 线程栈大小(单位:字节)
// 25: 线程优先级(数值越小,优先级越高)
// 10: 线程时间片
rt_thread_t thread1 = rt_thread_create("thread1", thread_entry1, RT_NULL, 1024, 25, 10);
// 创建线程2
// 参数说明:
// "thread2": 线程名称
// thread_entry2: 线程入口函数
// RT_NULL: 线程参数
// 1024: 线程栈大小(单位:字节)
// 25: 线程优先级(数值越小,优先级越高)
// 10: 线程时间片
rt_thread_t thread2 = rt_thread_create("thread2", thread_entry2, RT_NULL, 1024, 25, 10);
// 如果线程1创建成功,则启动线程1
if (thread1 != RT_NULL)
{
rt_thread_startup(thread1);
}
// 如果线程2创建成功,则启动线程2
if (thread2 != RT_NULL)
{
rt_thread_startup(thread2);
}
// 主函数返回0,表示正常结束
return 0;
}
实现效果:
