# 邮箱

## 数据竞争与同步问题

多线程环境下，如果多个线程同时读写⼀个全局变量，⽽没有使⽤锁或同步机制，会导致数据竞争，即多个线程同时操作全局变量，导致结果不确定或数据错误  

## 邮箱/消息队列的优势：

邮箱和消息队列都是线程安全的通信机制，能够保证消息传递的原⼦性和数据⼀致性。在使⽤这些⼯具时，操作系统会⾃动处理线程间的同步问题，不会发⽣类似的数据竞争。

如果 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;
}

```
实现效果：
![image-20250207154306309](https://typora-write.oss-cn-beijing.aliyuncs.com/20250211172841440.png)

**遇到的问题：**

```
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;
}
```
实现效果：
![image-20250207160620462](https://typora-write.oss-cn-beijing.aliyuncs.com/20250211172845452.png)

