鸿蒙南向开发教程 Day 7:互斥锁(Mutex)
·
目标:掌握 OpenHarmony 轻量系统的互斥锁 API,实现多线程共享资源的互斥访问
前置条件:已完成 Day 6 的事件标志组教程
一、工程结构
app/
├── BUILD.gn
└── 05_mutex/ # 模块目录
├── BUILD.gn
└── demo.c # 互斥锁测试代码
1.1 app/BUILD.gn
import("//build/lite/config/component/lite_component.gni")
lite_component("app") {
features = [
"05_mutex:mutex_demo", # 引用 05_mutex 模块
]
}
1.2 05_mutex/BUILD.gn
static_library("mutex_demo") {
sources = [
"demo.c"
]
include_dirs = [
"//utils/native/lite/include",
"//kernel/liteos_m/components/cmsis/2.0",
]
}
二、完整代码详解
2.1 头文件与宏定义
#include <stdio.h> // 标准输入输出
#include <unistd.h> // UNIX 标准函数
#include "ohos_init.h" // OpenHarmony 系统初始化
#include "cmsis_os2.h" // CMSIS-RTOS2 接口
#define STACK_SIZE (1024) // 线程栈大小
#define DELAY_TICKS_5 (5) // 5 tick 延时
#define DELAY_TICKS_13 (13) // 13 tick 延时
#define DELAY_TICKS_17 (17) // 17 tick 延时
#define NUMBER_2 (2) // 奇偶判断除数
#define NUMBER_100 (100) // 计数上限
static int g_test_value = 0; // 全局共享变量(临界资源)
关键:g_test_value 是全局共享变量,三个线程同时访问,必须用互斥锁保护,否则会出现竞态条件(Race Condition)。
2.2 工作线程(共享资源访问)
void number_thread(osMutexId_t *arg)
{
osMutexId_t *mid = arg; // 接收传入的互斥锁 ID 指针
while (g_test_value < NUMBER_100) {
// 1. 获取互斥锁,超时时间 100 tick
if (osMutexAcquire(*mid, NUMBER_100) == osOK) {
// 2. 进入临界区,访问共享资源
g_test_value++;
if (g_test_value % NUMBER_2 == 0) {
printf("[Mutex Test]%s gets an even value %d.\r\n",
osThreadGetName(osThreadGetId()), g_test_value);
} else {
printf("[Mutex Test]%s gets an odd value %d.\r\n",
osThreadGetName(osThreadGetId()), g_test_value);
}
// 3. 释放互斥锁,退出临界区
osMutexRelease(*mid);
// 4. 主动延时,让出 CPU,给其他线程竞争锁的机会
osDelay(DELAY_TICKS_5);
}
}
}
临界区保护流程:
[获取锁] → [访问 g_test_value] → [释放锁] → [延时让出 CPU]
↑________________________________________________↓
循环直到 g_test_value >= 100
2.3 线程创建辅助函数
osThreadId_t newThread(char *name, osThreadFunc_t func, osMutexId_t *arg)
{
osThreadAttr_t attr = {
name, // 线程名称
0, // 属性位
NULL, // 控制块内存
0, // 控制块大小
NULL, // 栈内存
STACK_SIZE * 2, // 栈大小 2048 字节
osPriorityNormal, // 普通优先级
0, // 保留
0 // 保留
};
osThreadId_t tid = osThreadNew(func, (void *)arg, &attr);
if (tid == NULL) {
printf("[Mutex Test]osThreadNew(%s) failed.\r\n", name);
} else {
printf("[Mutex Test]osThreadNew(%s) success, thread id: %d.\r\n", name, tid);
}
return tid;
}
2.4 主控制线程
void rtosv2_mutex_main(void)
{
// 1. 创建互斥锁
osMutexAttr_t attr = {0}; // 默认属性
osMutexId_t mid = osMutexNew(&attr);
if (mid == NULL) {
printf("[Mutex Test]osMutexNew, create mutex failed.\r\n");
} else {
printf("[Mutex Test]osMutexNew, create mutex success.\r\n");
}
// 2. 创建三个工作线程,共享同一个互斥锁
osThreadId_t tid1 = newThread("Thread_1", number_thread, &mid);
osThreadId_t tid2 = newThread("Thread_2", number_thread, &mid);
osThreadId_t tid3 = newThread("Thread_3", number_thread, &mid);
// 3. 延时 13 tick,让线程运行一段时间
osDelay(DELAY_TICKS_13);
// 4. 查询当前持有互斥锁的线程
osThreadId_t tid = osMutexGetOwner(mid);
printf("[Mutex Test]osMutexGetOwner, thread id: %p, thread name: %s.\r\n",
tid, osThreadGetName(tid));
// 5. 再延时 17 tick
osDelay(DELAY_TICKS_17);
// 6. 清理资源:终止线程 → 删除互斥锁
osThreadTerminate(tid1);
osThreadTerminate(tid2);
osThreadTerminate(tid3);
osMutexDelete(mid);
}
2.5 系统入口
static void MutexTestTask(void)
{
osThreadAttr_t attr = {
.name = "rtosv2_mutex_main",
.attr_bits = 0U,
.cb_mem = NULL,
.cb_size = 0U,
.stack_mem = NULL,
.stack_size = STACK_SIZE,
.priority = osPriorityNormal,
};
if (osThreadNew((osThreadFunc_t)rtosv2_mutex_main, NULL, &attr) == NULL) {
printf("[MutexTestTask] Failed to create rtosv2_mutex_main!\n");
}
}
APP_FEATURE_INIT(MutexTestTask);
三、CMSIS-RTOS2 互斥锁 API 详解
3.1 osMutexNew — 创建互斥锁
osMutexId_t osMutexNew(const osMutexAttr_t *attr);
| 参数 | 说明 |
|---|---|
attr |
互斥锁属性,NULL 或 {0} 为默认 |
默认属性特点:
- 支持递归锁定(同一线程可多次获取)
- 支持优先级继承(防止优先级反转)
3.2 osMutexAcquire — 获取互斥锁
osStatus_t osMutexAcquire(osMutexId_t mutex_id, uint32_t timeout);
| 参数 | 说明 |
|---|---|
mutex_id |
互斥锁 ID |
timeout |
超时时间(tick),osWaitForever = 永久等待 |
返回值:
| 状态 | 说明 |
|---|---|
osOK (0) |
获取成功 |
osErrorTimeout (-2) |
超时 |
osErrorResource (-3) |
资源不可用 |
osErrorParameter (-4) |
参数错误 |
行为:
- 锁空闲:立即获取,线程成为所有者
- 锁被占:线程进入 Blocked 状态,等待锁释放
- 同一线程递归获取:计数器 +1,不会死锁
3.3 osMutexRelease — 释放互斥锁
osStatus_t osMutexRelease(osMutexId_t mutex_id);
规则:
- 只有所有者线程才能释放
- 递归获取需对应次数释放
- 释放后,唤醒等待队列中优先级最高的线程
3.4 osMutexGetOwner — 获取当前所有者
osThreadId_t osMutexGetOwner(osMutexId_t mutex_id);
用途:调试、监控、死锁检测。
3.5 osMutexDelete — 删除互斥锁
osStatus_t osMutexDelete(osMutexId_t mutex_id);
注意:删除前确保没有线程持有该锁。
四、为什么需要互斥锁?—— 竞态条件演示
假设没有互斥锁,三个线程同时执行 g_test_value++:
初始: g_test_value = 0
Thread_1: 读取 g_test_value = 0 → 计算 0+1 = 1 → [被抢占]
Thread_2: 读取 g_test_value = 0 → 计算 0+1 = 1 → 写入 g_test_value = 1
Thread_1: [恢复] 写入 g_test_value = 1 ← 覆盖了 Thread_2 的结果!
结果: g_test_value = 1,但期望 = 2(两次++)
这种错误称为"竞态条件",在多核或抢占式调度中必然发生。
使用互斥锁后:
Thread_1: [Acquire] → 读取 0 → 计算 1 → 写入 1 → [Release]
Thread_2: 等待锁... [Acquire] → 读取 1 → 计算 2 → 写入 2 → [Release]
Thread_3: 等待锁... [Acquire] → 读取 2 → ...
结果: g_test_value 严格递增,无丢失
五、执行流程时序图
时间轴 →
main: [Create Mutex]──[Create T1,T2,T3]──[Delay 13]──[GetOwner]──[Delay 17]──[Terminate All]──[Delete Mutex]
↓ ↓ ↓ ↓
T1: [Acq]──[++=1]──[Rel]──[Dly5]──[Acq]──[++=2]──[Rel]──...──[++=100]──[退出]
↑ ↓ ↑ ↓
T2: 等待锁...──[Acq]──[++=...]──[Rel]──...──[++=100]──[退出]
↑ ↓ ↑
T3: 等待锁...──────────────[Acq]──[++=...]──[Rel]──...──[++=100]──[退出]
关键: 任意时刻只有一个线程在 [Acq...Rel] 临界区内
六、底层实现:LiteOS 原生互斥锁
CMSIS-RTOS2 的 osMutexXxx 在 LiteOS-M 中的映射:
| CMSIS-RTOS2 | LiteOS-M 原生 | 说明 |
|---|---|---|
osMutexNew |
LOS_MuxCreate |
创建互斥锁 |
osMutexAcquire |
LOS_MuxPend |
获取互斥锁 |
osMutexRelease |
LOS_MuxPost |
释放互斥锁 |
osMutexGetOwner |
LOS_MuxGetOwner |
获取所有者 |
osMutexDelete |
LOS_MuxDelete |
删除互斥锁 |
LiteOS-M 互斥锁特性:
- 优先级继承:低优先级线程持有锁时,临时提升其优先级,防止高优先级线程饥饿
- 递归锁定:同一线程可多次获取,需对应次数释放
- 死锁检测:部分版本支持
七、编译与验证
7.1 编译烧录
VSCode 点击 Build → Upload,串口波特率 115200。
7.2 预期输出
[Mutex Test]osMutexNew, create mutex success.
[Mutex Test]osThreadNew(Thread_1) success, thread id: 3.
[Mutex Test]osThreadNew(Thread_2) success, thread id: 4.
[Mutex Test]osThreadNew(Thread_3) success, thread id: 5.
[Mutex Test]Thread_1 gets an odd value 1.
[Mutex Test]Thread_2 gets an even value 2.
[Mutex Test]Thread_3 gets an odd value 3.
[Mutex Test]Thread_1 gets an even value 4.
...
[Mutex Test]osMutexGetOwner, thread id: 0x20001xxx, thread name: Thread_2.
...
[Mutex Test]Thread_3 gets an even value 100.
观察输出:三个线程交替获取奇偶值,无重复、无跳过,证明互斥锁工作正常。
八、总结
| 要点 | 内容 |
|---|---|
| 临界资源 | 全局变量 g_test_value,多线程共享 |
| 保护手段 | osMutexAcquire → 访问 → osMutexRelease |
| 超时机制 | osMutexAcquire(mid, timeout),避免永久阻塞 |
| 所有者查询 | osMutexGetOwner 用于调试监控 |
| 清理顺序 | 先终止线程,再删除互斥锁 |
| 底层映射 | LiteOS-M LOS_MuxXxx,支持优先级继承 |
九、下一步
Day 8 预告:信号量(Semaphore) —— 资源计数与任务同步。
更多推荐



所有评论(0)