freertos ----mutex 互斥锁例程
Mutex = 一把钥匙共享资源 = 一个上锁的箱子(库存)拿到钥匙的任务👉 才能动库存没钥匙的任务👉 必须等// 拿钥匙// 还钥匙// 👇👇👇 这里就是临界区// 👆👆👆👉被 Mutex 保护的代码块就叫临界区本程序通过 FreeRTOS 的 Mutex 机制,对多个任务访问共享库存变量的临界区进行保护,避免了由于任务并发执行导致的数据竞争和库存超卖问题。
/*
程序: Tasks之间数据传递
有多任务同时写入,或者数据大小超过cpu内存通道时,或者对共享资源的访问时候,需要有防范机制
使用MUTEX对数据对Cirtical Section的内容进行保护
可以想象成MUTEX就是一把锁
公众号:孤独的二进制
语法:
SemaphoreHandle_t xHandler; 创建Handler
xHandler = xSemaphoreCreateMutex(); 创建一个MUTEX 返回NULL,或者handler
xSemaphoreGive(xHandler); 释放
xSemaphoreTake(xHanlder, timeout); 指定时间内获取信号量 返回pdPASS, 或者pdFAIL
理解方法:
MUTEX的工作原理可以想象成
共享的资源被锁在了一个箱子里,只有一把钥匙,有钥匙的任务才能对改资源进行访问
*/
// 养成良好习惯,被多进程和中断调用的变量使用 volatile 修饰符
volatile uint32_t inventory = 100; //总库存
volatile uint32_t retailCount = 0; //线下销售量
volatile uint32_t onlineCount = 0; //线上销售量
SemaphoreHandle_t xMutexInventory = NULL; //创建信号量Handler
TickType_t timeOut = 1000; //用于获取信号量的Timeout 1000 ticks
void retailTask(void *pvParam) {
while (1) {
// 在timeout的时间内如果能够获取就继续
// 通俗一些:获取钥匙
if (xSemaphoreTake(xMutexInventory, timeOut) == pdPASS) {
//被MUTEX保护的内容叫做 Critical Section
//以下实现了带有随机延迟的 inventory减1;
//等效为 inventory--; retailCount++;
uint32_t inv = inventory;
for (int i; i < random(10, 100); i++) vTaskDelay(pdMS_TO_TICKS(i));
if (inventory > 0) {
inventory = inv - 1;
retailCount++;
//释放钥匙
xSemaphoreGive(xMutexInventory);
} else {
//无法获取钥匙
}
};
vTaskDelay(100); //老板要求慢一些,客户升级后,可以再加快速度
}
}
void onlineTask(void *pvParam) {
while (1) {
// 在timeout的时间内如果能够获取二进制信号量就继续
// 通俗一些:获取钥匙
if (xSemaphoreTake(xMutexInventory, timeOut) == pdPASS) {
//被MUTEX保护的内容叫做 Critical Section
//以下实现了带有随机延迟的 inventory减1;
//等效为 inventory--; retailCount++;
uint32_t inv = inventory;
for (int i; i < random(10, 100); i++) vTaskDelay(pdMS_TO_TICKS(i));
if (inventory > 0) {
inventory = inv - 1;
onlineCount++;
//释放钥匙
xSemaphoreGive(xMutexInventory);
} else {
//无法获取钥匙
}
};
vTaskDelay(100); //老板要求慢一些,客户升级后,可以再加快速度
}
}
void showTask(void *pvParam) {
while (1) {
printf("Inventory : %d\n", inventory);
printf(" Retail : %d, Online : %d\n", retailCount, onlineCount);
if (inventory == 0 ) {
uint32_t totalSales = retailCount + onlineCount;
printf("-----SALES SUMMARY-----\n");
printf(" Total Sales: %d\n", totalSales);
printf(" OverSales: %d\n", 100 - totalSales);
}
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
xMutexInventory = xSemaphoreCreateMutex(); //创建MUTEX
if (xMutexInventory == NULL) {
printf("No Enough Ram, Unable to Create Semaphore.");
} else {
xTaskCreate(onlineTask,
"Online Channel",
1024 * 4,
NULL,
1,
NULL);
xTaskCreate(retailTask,
"Retail Channel",
1024 * 4,
NULL,
1,
NULL);
xTaskCreate(showTask,
"Display Inventory",
1024 * 4,
NULL,
1,
NULL);
}
}
void loop() {
}
好,这段代码确实不太好懂,因为它同时涉及了 FreeRTOS 多任务 + 互斥锁(Mutex)+ 临界区 + 随机延时制造竞争。
我按**“先讲故事 → 再拆代码 → 再指出坑”**的方式给你讲一遍,你一定能懂。
一、先用「人话」讲这程序在干嘛
👉 这是一个“库存抢购”的模拟程序
-
只有 100 件库存
inventory -
有 两个销售渠道
-
线下卖(
retailTask) -
线上卖(
onlineTask)
-
-
三个任务同时运行
-
线下卖货任务
-
线上卖货任务
-
显示库存任务
-
⚠️ 问题:
两个任务同时卖货,如果不加锁,会发生“超卖 / 数据错乱”
所以:
👉 用 Mutex(互斥锁)来保护库存
二、核心概念先搞懂(非常关键)
1️⃣ 什么是 Mutex(互斥锁)
Mutex = 一把钥匙
共享资源 = 一个上锁的箱子(库存)
-
拿到钥匙的任务 👉 才能动库存
-
没钥匙的任务 👉 必须等
FreeRTOS 中:
xSemaphoreTake(xMutex, timeout); // 拿钥匙
xSemaphoreGive(xMutex); // 还钥匙
2️⃣ 什么是 Critical Section(临界区)
if (xSemaphoreTake(xMutexInventory, timeOut) == pdPASS) {
// 👇👇👇 这里就是临界区
inventory--;
retailCount++;
// 👆👆👆
xSemaphoreGive(xMutexInventory);
}
👉 被 Mutex 保护的代码块就叫临界区
三、全局变量:为什么要 volatile
volatile uint32_t inventory = 100;
volatile uint32_t retailCount = 0;
volatile uint32_t onlineCount = 0;
原因:
-
这些变量
-
被 多个任务
-
随时可能被修改
-
-
防止编译器优化导致:
-
任务 A 看不到任务 B 改的值
-
📌 口诀:
多任务 / 中断 / 共享变量 → volatile
四、Mutex 创建与参数
SemaphoreHandle_t xMutexInventory = NULL;
TickType_t timeOut = 1000; // 1000 个 tick 内等锁
xMutexInventory = xSemaphoreCreateMutex();
-
成功:返回一个 句柄(Handle)
-
失败:返回
NULL(内存不够)
五、重点:retailTask(线下卖货)逐行讲
1️⃣ 死循环(任务一直跑)
while (1) {
2️⃣ 尝试拿锁(钥匙)
if (xSemaphoreTake(xMutexInventory, timeOut) == pdPASS) {
含义:
-
最多等
1000 tick -
拿到锁 → 进入临界区
-
拿不到 → 什么也不做
3️⃣ 临界区(真正卖货)
uint32_t inv = inventory;
📌 先保存一份库存(模拟现实中的“读库存”)
4️⃣ 故意制造混乱(重点!)
for (int i; i < random(10, 100); i++)
vTaskDelay(pdMS_TO_TICKS(i));
👉 这是作者故意写的
目的:
-
拉长任务执行时间
-
增大两个任务同时访问库存的概率
-
用来证明:没有 Mutex 一定会出问题
5️⃣ 真正扣库存
if (inventory > 0) {
inventory = inv - 1;
retailCount++;
含义:
-
还有货才卖
-
库存 -1
-
线下销量 +1
6️⃣ 释放锁(非常重要)
xSemaphoreGive(xMutexInventory);
⚠️ 不释放 = 死锁(其他任务永远进不来)
7️⃣ 任务休息一下
vTaskDelay(100);
不然会一直疯狂抢锁
六、onlineTask:和 retailTask 一模一样
唯一差别:
onlineCount++;
👉 一个卖线上,一个卖线下,本质一样
七、showTask:显示任务(⚠️ 有隐患)
printf("Inventory : %d\n", inventory);
printf(" Retail : %d, Online : %d\n", retailCount, onlineCount);
❗ 这里没有加 Mutex
可能导致:
-
显示到一半,库存被改
-
打印的数据不一致
📌 严谨写法应该是:
xSemaphoreTake(xMutexInventory, portMAX_DELAY);
// 打印
xSemaphoreGive(xMutexInventory);
八、setup() 做了什么
Serial.begin(115200);
初始化串口
xMutexInventory = xSemaphoreCreateMutex();
创建锁
xTaskCreate(onlineTask, ...);
xTaskCreate(retailTask, ...);
xTaskCreate(showTask, ...);
创建三个并发任务
九、这程序想教你的“真正核心”
🔥 不是卖货,而是:
1️⃣ 多任务同时访问共享数据会出问题
2️⃣ Mutex 可以保护共享资源
3️⃣ 临界区一定要:
-
尽量短
-
必须释放锁
4️⃣volatile不是线程安全,只是“可见性”
十、我帮你一句话总结(考试/面试级)
本程序通过 FreeRTOS 的 Mutex 机制,对多个任务访问共享库存变量的临界区进行保护,避免了由于任务并发执行导致的数据竞争和库存超卖问题。
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐



所有评论(0)