当前位置: 首页 > news >正文

鸿蒙南向开发教程 Day 7:互斥锁(Mutex)

目标:掌握 OpenHarmony 轻量系统的互斥锁 API,实现多线程共享资源的互斥访问
前置条件:已完成 Day 6 的事件标志组教程


一、工程结构

app/ ├── BUILD.gn └── 05_mutex/ # 模块目录 ├── BUILD.gn └── demo.c # 互斥锁测试代码

1.1app/BUILD.gn

import("//build/lite/config/component/lite_component.gni") lite_component("app") { features = [ "05_mutex:mutex_demo", # 引用 05_mutex 模块 ] }

1.205_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 接口#defineSTACK_SIZE(1024)// 线程栈大小#defineDELAY_TICKS_5(5)// 5 tick 延时#defineDELAY_TICKS_13(13)// 13 tick 延时#defineDELAY_TICKS_17(17)// 17 tick 延时#defineNUMBER_2(2)// 奇偶判断除数#defineNUMBER_100(100)// 计数上限staticintg_test_value=0;// 全局共享变量(临界资源)

关键g_test_value全局共享变量,三个线程同时访问,必须用互斥锁保护,否则会出现竞态条件(Race Condition)。

2.2 工作线程(共享资源访问)

voidnumber_thread(osMutexId_t*arg){osMutexId_t*mid=arg;// 接收传入的互斥锁 ID 指针while(g_test_value<NUMBER_100){// 1. 获取互斥锁,超时时间 100 tickif(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_tnewThread(char*name,osThreadFunc_tfunc,osMutexId_t*arg){osThreadAttr_tattr={name,// 线程名称0,// 属性位NULL,// 控制块内存0,// 控制块大小NULL,// 栈内存STACK_SIZE*2,// 栈大小 2048 字节osPriorityNormal,// 普通优先级0,// 保留0// 保留};osThreadId_ttid=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);}returntid;}

2.4 主控制线程

voidrtosv2_mutex_main(void){// 1. 创建互斥锁osMutexAttr_tattr={0};// 默认属性osMutexId_tmid=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_ttid1=newThread("Thread_1",number_thread,&mid);osThreadId_ttid2=newThread("Thread_2",number_thread,&mid);osThreadId_ttid3=newThread("Thread_3",number_thread,&mid);// 3. 延时 13 tick,让线程运行一段时间osDelay(DELAY_TICKS_13);// 4. 查询当前持有互斥锁的线程osThreadId_ttid=osMutexGetOwner(mid);printf("[Mutex Test]osMutexGetOwner, thread id: %p, thread name: %s.\r\n",tid,osThreadGetName(tid));// 5. 再延时 17 tickosDelay(DELAY_TICKS_17);// 6. 清理资源:终止线程 → 删除互斥锁osThreadTerminate(tid1);osThreadTerminate(tid2);osThreadTerminate(tid3);osMutexDelete(mid);}

2.5 系统入口

staticvoidMutexTestTask(void){osThreadAttr_tattr={.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.1osMutexNew— 创建互斥锁

osMutexId_tosMutexNew(constosMutexAttr_t*attr);
参数说明
attr互斥锁属性,NULL 或{0}为默认

默认属性特点

  • 支持递归锁定(同一线程可多次获取)
  • 支持优先级继承(防止优先级反转)

3.2osMutexAcquire— 获取互斥锁

osStatus_tosMutexAcquire(osMutexId_tmutex_id,uint32_ttimeout);
参数说明
mutex_id互斥锁 ID
timeout超时时间(tick),osWaitForever= 永久等待

返回值

状态说明
osOK(0)获取成功
osErrorTimeout(-2)超时
osErrorResource(-3)资源不可用
osErrorParameter(-4)参数错误

行为

  • 锁空闲:立即获取,线程成为所有者
  • 锁被占:线程进入Blocked状态,等待锁释放
  • 同一线程递归获取:计数器 +1,不会死锁

3.3osMutexRelease— 释放互斥锁

osStatus_tosMutexRelease(osMutexId_tmutex_id);

规则

  • 只有所有者线程才能释放
  • 递归获取需对应次数释放
  • 释放后,唤醒等待队列中优先级最高的线程

3.4osMutexGetOwner— 获取当前所有者

osThreadId_tosMutexGetOwner(osMutexId_tmutex_id);

用途:调试、监控、死锁检测。

3.5osMutexDelete— 删除互斥锁

osStatus_tosMutexDelete(osMutexId_tmutex_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-RTOS2LiteOS-M 原生说明
osMutexNewLOS_MuxCreate创建互斥锁
osMutexAcquireLOS_MuxPend获取互斥锁
osMutexReleaseLOS_MuxPost释放互斥锁
osMutexGetOwnerLOS_MuxGetOwner获取所有者
osMutexDeleteLOS_MuxDelete删除互斥锁

LiteOS-M 互斥锁特性:

  • 优先级继承:低优先级线程持有锁时,临时提升其优先级,防止高优先级线程饥饿
  • 递归锁定:同一线程可多次获取,需对应次数释放
  • 死锁检测:部分版本支持

七、编译与验证

7.1 编译烧录

VSCode 点击BuildUpload,串口波特率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-MLOS_MuxXxx,支持优先级继承

九、下一步

Day 8 预告:信号量(Semaphore)—— 资源计数与任务同步。

http://www.gsyq.cn/news/1456217.html

相关文章:

  • 北京 10 家防水补漏商家深度测评|卫生间、外墙、屋顶漏水维修怎么选?鑫兴晟达综合实力稳居榜首 - 吉林同城获客
  • Ultimate Vocal Remover:5分钟快速掌握AI音频分离的终极指南
  • Meta-Llama-3-8B-Instruct全面解析:Meta革命性80亿参数对话模型深度评测
  • 生命在于变化的庖丁解牛
  • 2026年智能温控系统厂家推荐排行榜:精准控温与节能芯片技术实力深度解析 - 品牌企业推荐师(官方)
  • 基于TI C2000的电动赛车数据采集系统:从传感器到可视化全链路设计
  • 2026专业测评!北京木质家具遭天牛蛀蚀?16区正规消杀公司深度对比 - 苏易修缮
  • 2026年6月热门的短视频运营推荐,工厂短视频陪跑/短视频陪跑/制造业宣传片制作/工厂短视频制作,短视频运营多少钱一个月 - 品牌推荐师
  • 零基础玩转RVC语音克隆:5分钟打造专属AI声线
  • 2026 北京防水补漏 10 家商家实测测评|卫生间 / 外墙 / 屋顶 / 地下室渗漏维修优选指南 - 吉林同城获客
  • 【Excel数据工程实战】从 #N/A 到透视表漂移:一套可复用的排错与重构流程(附 Power Query 方案)
  • KMS智能激活解决方案:Windows与Office的终极免费激活指南
  • 2026北京防虫排名!家里有天牛怎么彻底根除?16区3家专业团队对比 - 苏易修缮
  • 主题模型在量化交易中的应用:GitHub_Trending/ma/machine-learning-for-trading LDA实战
  • DAIHEN AGA-27B 60MHz High Power Source 电源日本
  • 2026年在线悬浮物浓度计十大品牌权威排行榜:专业选型指南与深度技术解析 - 水质仪表品牌排行榜
  • 自然语言查询系统实战:从架构设计到工程落地的完整指南
  • 从手动到自动:WinUtil如何将Windows系统管理效率提升500%
  • Mobile-Agent架构深度解析:跨平台智能调度引擎的技术突破与实践指南
  • 基于GreenPAK的温度-频率转换器设计:用数字逻辑实现低成本温度监测
  • 从零开始:用Vin象棋AI助手3分钟打造你的私人象棋教练
  • PDF补丁丁终极指南:10个免费PDF处理技巧让你工作效率翻倍
  • 私藏找靠谱美发店必看!2026全年度高口碑深圳发型师推荐:6月烫头发/漂染头发/接发理发店哪家好揭秘!附发型师怎么选FAQ避坑要点! - 奋斗者888
  • 2026年除湿系统厂家推荐榜单:工业/商用/家用除湿机源头工厂,精准控湿与节能实力品牌深度解析! - 品牌企业推荐师(官方)
  • Windows系统优化工具箱:从手动配置到一键自动化
  • PyTorch自定义损失报错怎么办?教你一招避坑
  • 2026年6月干线物流自动驾驶「车路运能」一体化综合实力测评 - 外贸老黄
  • InfluxDB 生产环境实战:降采样、数据保留策略与 Flux 查询语言深度解析
  • OptiScaler终极指南:打破硬件限制的游戏超分辨率与帧生成解决方案
  • 有哪些AI论文网站是真的贴合学术规范,而不是通用套壳?