RT-Thread快速入门(七)之互斥量
前言在上篇文章的信号量示例中我们把信号量初始值设为 1用做二值信号量也能实现互斥访问线程A take(信号量) → 使用资源 → release(信号量) 线程B take(信号量) → 阻塞等待 → …… → 被唤醒但这有一个隐患任何线程都可以 release 这个信号量包括不是它持有者的第三方线程。这就好比你把厕所门锁了外面的人却能把手伸进来开锁——那锁还有什么意义互斥量Mutex正是为了解决这个问题而生的。它的核心规则是谁上锁就只能由谁开锁。目录前言一、为什么要互斥1.1 临界资源1.2 什么情况下需要互斥二、互斥量的特性2.1 互斥量 vs 二值信号量2.2 互斥量的核心优势三、优先级反转与优先级继承3.1 什么是优先级反转3.2 优先级继承 — RT-Thread 的解决方案四、互斥量 API4.1 创建/初始化4.2 删除/脱离4.3 获取/释放五、完整示例示例1互斥量基本使用示例2优先级反转与继承演示六、互斥量使用注意事项七、IPC 全家桶总结1~7篇八、总结一、为什么要互斥1.1 临界资源在多线程系统中如果多个线程同时访问同一个资源就可能出问题/* 两个线程同时调用这个函数结果会怎样 */ static int a 1; ​ void add_a(void) { a a 8; // 内部三步①读原值 ②加8 ③写回 }如果线程 A 读出了a1还没写回这时线程 B 抢占了 CPU也读了a1然后加 8 写回 →a9。接着 A 继续执行它手里的值还是 1加 8 写回 →a9。期望结果是 17实际结果是 9。这就是典型的非原子操作导致的竞态问题。1.2 什么情况下需要互斥场景例子说明访问外设串口打印两个线程同时打印信息混杂在一起修改变量a a 8读-改-写三步可能被中断访问结构体设置多个成员设置到一半被抢占结构体状态不一致函数重入使用全局变量的函数多个线程同时调用全局变量互相覆盖这些需要被独占访问的资源称为临界资源。访问临界资源的那段代码称为临界区。二、互斥量的特性2.1 互斥量 vs 二值信号量对比项二值信号量互斥量Mutex初始值1/01谁可以释放任何线程/ISR都可以只有持有者可以释放中断中可用✅ 可以❌不可用优先级继承❌ 不支持✅支持解决优先级反转本质计数器取 0 或 1特殊的二值信号量拥有者标记2.2 互斥量的核心优势信号量线程A take → 串口打印 → 线程B 也能 release → 线程A 被强制解锁互斥量线程A take → 串口打印 → 只有线程A 能 release → 别人无法解锁互斥量就像一把带指纹识别的锁——只有上锁的人才能打开。三、优先级反转与优先级继承3.1 什么是优先级反转假设有三个线程LPThread低优先级15MPThread中优先级14HPThread高优先级13正常预期HPThread(13) MPThread(14) LPThread(15) 实际可能 ① LPThread 先获得互斥量 ② HPThread 也想获得互斥量 → 被阻塞LPThread 不释放 ③ MPThread 不需要互斥量但优先级比 LPThread 高 → MPThread 一直运行LPThread 无法执行 → LPThread 无法释放互斥量 → HPThread 被 LPThread 堵住又被 MPThread 间接堵住结果最高优先级的 HPThread被中优先级的 MPThread 间接阻塞——这叫优先级反转。3.2 优先级继承 — RT-Thread 的解决方案RT-Thread 的互斥量内置了优先级继承机制① LPThread 获得互斥量 → 优先级 15 ② HPThread 也想获得互斥量 → 失败 → HPThread 把 LPThread 的优先级提升到自己一样 → LPThread 的优先级从 15 提升到 13 ③ LPThread 变成最高优先级 → 立刻执行 ④ LPThread 释放互斥量 → 恢复自己的优先级 15 ⑤ HPThread 被唤醒 → 获得互斥量 → 执行优先级继承 低优先级线程临时借用高优先级线程的优先级以便尽快释放互斥量。四、互斥量 API4.1 创建/初始化/* 动态创建 */ rt_mutex_t rt_mutex_create(const char *name, rt_uint8_t flag); ​ /* 静态初始化 */ rt_err_t rt_mutex_init(rt_mutex_t mutex, const char *name, rt_uint8_t flag);⚠️注意flag参数已废除均按RT_IPC_FLAG_PRIO处理优先级继承必须基于优先级排序。4.2 删除/脱离rt_err_t rt_mutex_delete(rt_mutex_t mutex); // 动态删除 rt_err_t rt_mutex_detach(rt_mutex_t mutex); // 静态脱离4.3 获取/释放/* 获取互斥量可阻塞 */ rt_err_t rt_mutex_take(rt_mutex_t mutex, rt_int32_t time); ​ /* 尝试获取不阻塞 */ rt_err_t rt_mutex_trytake(rt_mutex_t mutex); ​ /* 释放互斥量只能由持有者调用 */ rt_err_t rt_mutex_release(rt_mutex_t mutex);五、完整示例示例1互斥量基本使用本程序创建 2 个发送线程通过宏USE_MUTEX切换是否使用互斥量观察串口打印是否混杂。#include rtthread.h ​ #define USE_MUTEX 1 // 1使用互斥量0不使用 ​ static rt_mutex_t dynamic_mutex RT_NULL; static rt_thread_t thread1 RT_NULL; static rt_thread_t thread2 RT_NULL; ​ #define THREAD_PRIORITY 15 #define THREAD_STACK_SIZE 512 #define THREAD_TIMESLICE 10 ​ /* 发送线程入口函数两个线程共用 */ static void thread_entry(void *parameter) { rt_uint32_t i; ​ while(1) { #ifdef USE_MUTEX /* 使用互斥量完整打印 0~19不会被其他线程打断 */ rt_mutex_take(dynamic_mutex, RT_WAITING_FOREVER); ​ for(i 0; i 20; i) rt_kprintf(%s: i%d\n, (char *)parameter, i); ​ rt_mutex_release(dynamic_mutex); #else /* 不使用互斥量打印到一半可能被其他线程打断 */ for(i 0; i 20; i) rt_kprintf(%s: i%d\n, (char *)parameter, i); #endif ​ rt_thread_mdelay(10); } } ​ int main(void) { /* 1. 创建动态互斥量 */ dynamic_mutex rt_mutex_create(dmutex, RT_IPC_FLAG_FIFO); if (dynamic_mutex RT_NULL) { rt_kprintf(rt_mutex_create error.\n); return -1; } ​ /* 2. 创建发送线程1 */ thread1 rt_thread_create(thread1, thread_entry, thread1, THREAD_STACK_SIZE, THREAD_PRIORITY, THREAD_TIMESLICE); if (thread1 ! RT_NULL) rt_thread_startup(thread1); ​ /* 3. 创建发送线程2 */ thread2 rt_thread_create(thread2, thread_entry, thread2, THREAD_STACK_SIZE, THREAD_PRIORITY, THREAD_TIMESLICE); if (thread2 ! RT_NULL) rt_thread_startup(thread2); ​ return 0; }运行结果对比未用互斥量 用互斥量不使用实验现象如下图左边所示线程 1、线程 2 的打印信息没有混在 一起线程1打印0~7还未到19就被线程2打断使用实验现象如下图右边所示线程1和线程2依次打印0~19中途 不会相互打断示例2优先级反转与继承演示创建三个线程演示优先级继承机制#include rtthread.h ​ static rt_mutex_t dynamic_mutex RT_NULL; static rt_thread_t LPThread RT_NULL; // 低优先级 15 static rt_thread_t MPThread RT_NULL; // 中优先级 14 static rt_thread_t HPThread RT_NULL; // 高优先级 13 ​ #define THREAD_PRIORITY 15 #define THREAD_STACK_SIZE 512 #define THREAD_TIMESLICE 10 ​ /* 低优先级线程获得互斥量然后被 HPThread 提升优先级 */ static void lpthread_entry(void *parameter) { rt_kprintf(LPThread: get mutex\n); ​ rt_mutex_take(dynamic_mutex, RT_WAITING_FOREVER); ​ /* 此时的优先级可能已被 HPThread 提升 */ rt_kprintf(LPThread: do something\n); rt_thread_mdelay(5); ​ rt_mutex_release(dynamic_mutex); rt_kprintf(LPThread: release mutex\n); } ​ /* 中优先级线程不需要互斥量 */ static void mpthread_entry(void *parameter) { rt_kprintf(MPThread: run\n); rt_thread_mdelay(2); // 短暂运行后挂起 } ​ /* 高优先级线程需要互斥量 */ static void hpthread_entry(void *parameter) { rt_kprintf(HPThread: try to get mutex\n); ​ rt_mutex_take(dynamic_mutex, RT_WAITING_FOREVER); rt_kprintf(HPThread: got mutex\n); ​ rt_mutex_release(dynamic_mutex); } ​ int main(void) { /* 1. 创建动态互斥量 */ dynamic_mutex rt_mutex_create(dmutex, RT_IPC_FLAG_FIFO); if (dynamic_mutex RT_NULL) return -1; ​ /* 2. 创建三个优先级不同的线程 */ LPThread rt_thread_create(LPThread, lpthread_entry, RT_NULL, THREAD_STACK_SIZE, THREAD_PRIORITY, // 15 THREAD_TIMESLICE); if (LPThread ! RT_NULL) rt_thread_startup(LPThread); ​ MPThread rt_thread_create(MPThread, mpthread_entry, RT_NULL, THREAD_STACK_SIZE, THREAD_PRIORITY - 1, // 14 THREAD_TIMESLICE); if (MPThread ! RT_NULL) rt_thread_startup(MPThread); ​ HPThread rt_thread_create(HPThread, hpthread_entry, RT_NULL, THREAD_STACK_SIZE, THREAD_PRIORITY - 2, // 13 THREAD_TIMESLICE); if (HPThread ! RT_NULL) rt_thread_startup(HPThread); ​ return 0; }执行流程时序事件优先级变化AHPThread(13) 先运行设置标志后休眠 3ms13BMPThread(14) 运行休眠 1ms14CLPThread(15) 获得互斥量开始运行15DMPThread 休眠结束 → 抢占 LPThread14 vs 15EHPThread 休眠结束 → 需要互斥量 → 失败 →提升LPThread优先级LPThread: 15→13FLPThread(13) 变成最高优先级 → 继续运行 → 释放互斥量 →恢复原优先级唤醒 HPThreadLPThread: 13→15GHPThread(13) 获得互斥量执行完毕13六、互斥量使用注意事项注意事项说明❌不能在中断中使用ISR 中不可调用rt_mutex_take/release✅只能由持有者释放其他线程无法强制释放✅支持优先级继承自动解决优先级反转问题✅嵌套取锁同一个线程可以多次take但需要相同次数的release⚠️尽快释放持有互斥量期间不要做耗时操作七、IPC 全家桶总结1~7篇到现在为止我们已经学完了 RT-Thread 的所有核心 IPC 机制文章内容传递什么特点一线程创建与删除—线程基础二优先级与时间片—调度规则三延时函数与空闲线程—让出 CPU四消息队列任意大小数据灵活链表实现五邮箱4 字节整数/指针轻量数组实现六信号信号编号软中断异步通知六信号量1 个计数值计数/同步七互斥量—资源互斥优先级继承八、总结知识点要点互斥量特殊的二值信号量自带所有权标记核心规则谁 take谁 release别人无法释放动态创建rt_mutex_create(name, flag)— flag 已废除静态初始化rt_mutex_init(mutex, name, flag)获取rt_mutex_take()可阻塞/rt_mutex_trytake()不阻塞释放rt_mutex_release()— 只能是持有者调用删除/脱离rt_mutex_delete()/rt_mutex_detach()⚠️ 中断中不可使用互斥量✅ 优先级继承自动解决优先级反转问题互斥量 vs 信号量信号量用于计数/同步互斥量用于资源互斥

相关新闻

硅胶包胶的难点在哪里:固转液复合工艺拆解

硅胶包胶的难点在哪里:固转液复合工艺拆解

“硅胶包胶"这个词在代工询价里出现频率很高,但很多人对它的理解就是"把硅胶包在别的东西外面”。实际上,硅胶包胶是LSR代工里技术难度偏高的一类工艺,难不在"包"这个动作本身,而在于让硅胶和基材真正粘在一起…

2026/7/13 14:57:13 阅读更多 →
三步解锁Wand专业版:终极免费增强工具让你的游戏修改体验更完美

三步解锁Wand专业版:终极免费增强工具让你的游戏修改体验更完美

三步解锁Wand专业版:终极免费增强工具让你的游戏修改体验更完美 【免费下载链接】Wand-Enhancer Advanced UX and interoperability extension for Wand (WeMod) app 项目地址: https://gitcode.com/GitHub_Trending/we/Wand-Enhancer 还在为游戏修改工具Wan…

2026/7/13 14:57:13 阅读更多 →
2026年电子合同行业五大趋势:从“能用“到“智能“的三级跳

2026年电子合同行业五大趋势:从“能用“到“智能“的三级跳

摘要:2026年电子合同行业正经历从"能用"到"好用"再到"智能"的加速转型。本文系统梳理了五大核心趋势:1) AI深度介入合同全生命周期,从辅助工具升级为智能中台;2) 区块链存证从概念走向刚需&#xf…

2026/7/13 14:57:13 阅读更多 →

最新新闻

Gammazero/Deque社区贡献指南:如何参与这个高性能队列项目的开发

Gammazero/Deque社区贡献指南:如何参与这个高性能队列项目的开发

Gammazero/Deque社区贡献指南:如何参与这个高性能队列项目的开发 【免费下载链接】deque Fast ring-buffer deque (double-ended queue) 项目地址: https://gitcode.com/gh_mirrors/deque3/deque Gammazero/Deque是一个高性能的环形缓冲区双端队列实现&#…

2026/7/13 15:46:17 阅读更多 →
Swift Metrics部署指南:生产环境中的配置、调优和运维最佳实践

Swift Metrics部署指南:生产环境中的配置、调优和运维最佳实践

Swift Metrics部署指南:生产环境中的配置、调优和运维最佳实践 【免费下载链接】swift-metrics Metrics API for Swift 项目地址: https://gitcode.com/gh_mirrors/sw/swift-metrics Swift Metrics是Swift服务器生态系统中的核心监控API标准,为生…

2026/7/13 15:44:17 阅读更多 →
仅限本周开放|Claude长文档自动切片+语义锚点标注工具链(含Python脚本+JSON Schema模板)

仅限本周开放|Claude长文档自动切片+语义锚点标注工具链(含Python脚本+JSON Schema模板)

更多请点击: https://kaifayun.com 第一章:Claude长文档总结的核心挑战与认知重构 处理超长文档时,Claude模型面临三重结构性张力:上下文窗口的物理边界、语义连贯性的断裂风险,以及关键信息在冗余文本中的稀释效应。…

2026/7/13 15:44:17 阅读更多 →
Chatterbox TTS架构设计实战:企业级语音合成解决方案

Chatterbox TTS架构设计实战:企业级语音合成解决方案

Chatterbox TTS架构设计实战:企业级语音合成解决方案 【免费下载链接】chatterbox SoTA open-source TTS 项目地址: https://gitcode.com/GitHub_Trending/chatterbox7/chatterbox Chatterbox作为Resemble AI开发的开源State-of-the-Art(SoTA&…

2026/7/13 15:38:15 阅读更多 →
免费解锁Wand专业版终极指南:三步告别限制,畅享完整游戏修改体验

免费解锁Wand专业版终极指南:三步告别限制,畅享完整游戏修改体验

免费解锁Wand专业版终极指南:三步告别限制,畅享完整游戏修改体验 【免费下载链接】Wand-Enhancer Advanced UX and interoperability extension for Wand (WeMod) app 项目地址: https://gitcode.com/GitHub_Trending/we/Wand-Enhancer 你是否厌倦…

2026/7/13 15:36:13 阅读更多 →
Dism++终极指南:免费Windows系统优化神器完整使用教程

Dism++终极指南:免费Windows系统优化神器完整使用教程

Dism终极指南:免费Windows系统优化神器完整使用教程 【免费下载链接】Dism-Multi-language Dism Multi-language Support & BUG Report 项目地址: https://gitcode.com/gh_mirrors/di/Dism-Multi-language Dism是一款免费开源的Windows系统优化工具&…

2026/7/13 15:29:46 阅读更多 →

日新闻

Palworld存档编辑完全掌握:从零开始实现游戏数据可视化修改

Palworld存档编辑完全掌握:从零开始实现游戏数据可视化修改

Palworld存档编辑完全掌握:从零开始实现游戏数据可视化修改 【免费下载链接】palworld-save-tools Tools for converting Palworld .sav files to JSON and back 项目地址: https://gitcode.com/gh_mirrors/pa/palworld-save-tools 你是否曾经想要调整Palwor…

2026/7/13 0:01:19 阅读更多 →
浦东旧模块回收哪家强?专业评测带你一探究竟

浦东旧模块回收哪家强?专业评测带你一探究竟

于科技迅猛飞速迭代的当下此刻, 旧模块的回收处置, 不但关联着资源的再度利用, 而且更牵扯到数据安全以及环保合规事宜。你是不是也正为那堆积得如同山峦般的旧模块而发愁? 是不是不清楚该怎样安全且高效地去处理它们? 别忧心烦恼, 就在今日, 我会以具备权威影响力的自媒体博…

2026/7/13 0:01:19 阅读更多 →
卡梅德生物技术快报|重组蛋白的表达和纯化:IMAC 金属螯合色谱全流程工艺手册|基质 - 配基 - 金属离子匹配与蛋白质分离纯化参数优化

卡梅德生物技术快报|重组蛋白的表达和纯化:IMAC 金属螯合色谱全流程工艺手册|基质 - 配基 - 金属离子匹配与蛋白质分离纯化参数优化

1 研究背景与现存技术痛点(提出问题)基因工程、蛋白质组学、生物制药研发流程中,蛋白质分离纯化是决定下游实验成败的关键环节。当前实验室常规蛋白质分离纯化工艺存在三类难以标准化的技术瓶颈:传统离子交换、分子筛层析无特异性…

2026/7/13 0:05:20 阅读更多 →

周新闻

互联网大厂 Java 求职面试:燕双非的搞笑回答与技术探讨

互联网大厂 Java 求职面试:燕双非的搞笑回答与技术探讨

互联网大厂 Java 求职面试:燕双非的搞笑回答与技术探讨 在一个阳光明媚的上午,互联网大厂的面试官坐在桌前,准备迎接他的面试候选人——燕双非,一个以搞笑和幽默著称的程序员。第一轮提问 面试官:燕双非,作…

2026/7/13 4:38:36 阅读更多 →
车载以太网PMA测试设备选型:示波器、VNA、信号源3类仪器关键参数与预算评估

车载以太网PMA测试设备选型:示波器、VNA、信号源3类仪器关键参数与预算评估

车载以太网PMA测试设备选型:示波器、VNA、信号源3类仪器关键参数与预算评估在智能驾驶和车联网技术快速发展的今天,车载以太网作为新一代车载网络的核心传输技术,其物理层性能直接决定了数据传输的可靠性和稳定性。1000BASE-T1作为当前主流的…

2026/7/13 4:38:38 阅读更多 →
VSCode EIDE 插件 2.0:APM32/STM32 项目迁移实战,5步完成Keil工程转换

VSCode EIDE 插件 2.0:APM32/STM32 项目迁移实战,5步完成Keil工程转换

VSCode EIDE 插件 2.0:APM32/STM32 项目迁移实战指南嵌入式开发领域正经历一场工具链的静默革命。当传统Keil用户首次打开VSCode的扩展市场搜索EIDE时,往往会惊讶于这个看似简单的插件竟能重构十余年的开发习惯。本文将揭示如何用五个精准步骤&#xff0…

2026/7/13 4:38:40 阅读更多 →

月新闻