RT-Thread 之互斥量使用

news/2025/10/27 0:25:55/文章来源:https://www.cnblogs.com/lsksp/p/19167661

一、互斥量概述

互斥量(Mutex)是 RT-Thread 中用于解决线程间共享资源独占访问的核心 IPC 机制,核心特性是 “二值状态” 与 “优先级继承”:仅允许同一时间一个线程持有互斥量(类似 “独占锁”),且当低优先级线程持有互斥量时,若高优先级线程请求该资源,低优先级线程会临时继承高优先级,避免 “优先级反转” 问题。

典型应用场景

  • 多线程竞争硬件外设(如 LED、UART、SPI 总线),防止并发操作导致的设备异常;

  • 多线程读写全局数据(如传感器采集缓存、配置参数),避免数据错乱;

  • 实时性要求高的场景(如工业控制中高优先级控制线程与低优先级日志线程共享资源)。

二、互斥量核心 API 函数

互斥量的操作围绕 “创建 / 初始化 / 获取 / 释放 / 删除 / 脱离” 展开,需区分动态创建(依赖内存堆)与静态初始化(基于全局 / 静态变量),核心参数与功能如下表所示。

2.1 互斥量的创建与初始化

类型 函数原型 关键参数说明
动态创建 rt_mutex_t rt_mutex_create(const char *name, rt_uint8_t flag) - name:互斥量名称(用于 FinSH 调试,如 “mutex_test”)- flag:等待队列排序方式(RT_IPC_FLAG_FIFO= 先进先出;RT_IPC_FLAG_PRIO= 按线程优先级)
静态初始化 rt_err_t rt_mutex_init(rt_mutex_t mutex, const char *name, rt_uint8_t flag) - mutex:全局 / 静态定义的互斥量对象(如struct rt_mutex mutex_test)- 其他参数同动态创建

2.2 互斥量的获取

获取互斥量即 “申请共享资源”,若互斥量未被持有则直接占用,若已被持有则线程进入阻塞状态,直到超时或资源释放。

函数 功能描述
rt_err_t rt_mutex_take(rt_mutex_t mutex, rt_int32_t timeout) 带超时获取:timeout= 等待时间(单位:tick,RT_WAITING_FOREVER= 永久等待,0= 无等待)
rt_err_t rt_mutex_trytake(rt_mutex_t mutex) 无等待获取:若互斥量不可用,直接返回RT_EBUSY,不阻塞线程

2.3 互斥量的释放与销毁

释放互斥量即 “归还共享资源”,仅允许持有互斥量的线程调用;销毁 / 脱离用于回收互斥量占用的资源。

操作 函数原型 适用场景
释放 rt_err_t rt_mutex_release(rt_mutex_t mutex) 线程使用完资源后,归还互斥量
动态删除 rt_err_t rt_mutex_delete(rt_mutex_t mutex) 销毁动态创建的互斥量(释放堆内存)
静态脱离 rt_err_t rt_mutex_detach(rt_mutex_t mutex) 脱离静态初始化的互斥量(不释放内存)

三、互斥量使用示例

3.1 源代码

#include "thread_task.h"
#include "main.h"
#include <stdio.h>      
#include "rtthread.h"
#include <rthw.h>/******************************************** 线程 1 ******************************************************/
#define THREAD_1_PRIORITY  		4           /* 线程优先级(值越小优先级越高) */
#define THREAD_1_STACK_SIZE		512         /* 线程栈空间大小(单位:字节) */
#define THREAD_1_TIMESLICE		10           /* 线程时间片个数(单位:tick) */
static struct rt_thread *thread_1_handle;    /* 线程句柄 *//******************************************** 线程 2 ******************************************************/
#define THREAD_2_PRIORITY  		5           /* 线程优先级(低于线程1) */
#define THREAD_2_STACK_SIZE		512         /* 线程栈空间大小 */
#define THREAD_2_TIMESLICE		10           /* 线程时间片个数 */
static struct rt_thread *thread_2_handle;    /* 线程句柄 */struct rt_mutex mutex_test;  /* 静态互斥量对象(全局定义,提前分配内存) *//*** @brief  LED闪烁函数(固定闪烁6次,即3个完整周期)* @param  time:每次翻转后的延迟时间(单位:tick)*/
void LED_toggle(uint16_t time)
{   for(uint8_t i = 0; i < 6; i++)  {HAL_GPIO_TogglePin(GPIOC, LED1_Pin);rt_thread_delay(time);      }
}/*** @brief  线程1入口函数(高优先级,10Hz闪烁LED)* @param  param:线程参数(未使用,设为RT_NULL)*/
void thread_1_entry(void* param)
{while(1){/* 带100ms超时获取互斥量:非关键任务,超时后放弃,避免阻塞 */if (rt_mutex_take(&mutex_test, 100) == RT_EOK){LED_toggle(100);/* 释放互斥量,归还资源 */            rt_mutex_release(&mutex_test);}rt_thread_delay(200); }
}/*** @brief  线程2入口函数(低优先级,1Hz闪烁LED)* @param  param:线程参数(未使用,设为RT_NULL)*/
void thread_2_entry(void* param)
{   while(1){/* 永久等待获取互斥量:关键任务,必须拿到资源才执行 */if (rt_mutex_take(&mutex_test, RT_WAITING_FOREVER) == RT_EOK){LED_toggle(1000); rt_mutex_release(&mutex_test);  }rt_thread_delay(300); }
}/*** @brief  初始化互斥量并创建启动线程(先初始化互斥量,再启动线程)*/
void ThreadStart(void)
{rt_base_t level = rt_hw_interrupt_disable();  /* 静态初始化互斥量 */rt_mutex_init(&mutex_test,            /* 互斥量对象 */"mutex_test",         /* 互斥量名称(用于finsh调试) */RT_IPC_FLAG_FIFO       /* 等待队列按FIFO排序(也可使用 RT_IPC_FLAG_PRIO) */);/* 动态创建并启动线程1 */thread_1_handle = rt_thread_create("thread_1",				/* 线程名称 */thread_1_entry,			/* 线程入口函数 */RT_NULL,				/* 线程参数 */THREAD_1_STACK_SIZE,	/* 线程栈大小 */THREAD_1_PRIORITY,		/* 线程优先级 */THREAD_1_TIMESLICE  	/* 线程时间片 */);if (thread_1_handle != RT_NULL)rt_thread_startup(thread_1_handle);  /* 动态创建并启动线程2 */thread_2_handle = rt_thread_create("thread_2",				/* 线程名称 */thread_2_entry,			/* 线程入口函数 */RT_NULL,				/* 线程参数 */THREAD_2_STACK_SIZE,	/* 线程栈大小 */THREAD_2_PRIORITY,		/* 线程优先级 */THREAD_2_TIMESLICE  	/* 线程时间片 */);if (thread_2_handle != RT_NULL)rt_thread_startup(thread_2_handle);  rt_hw_interrupt_enable(level);  
}

3.2 代码执行流程

3.2.1 初始阶段(资源初始化)

  1. 调用ThreadStart时,先关闭中断,避免互斥量初始化被打断(原子操作);

  2. 静态初始化互斥量mutex_test,等待队列按 FIFO 排序,初始状态为 “未被持有”;

  3. 动态创建线程 1(优先级 4)和线程 2(优先级 5),创建成功后启动线程;

  4. 恢复中断,RT-Thread 调度器开始工作,优先调度高优先级线程。

3.2.2 竞争阶段(高优先级线程优先)

  1. 线程 1 优先级高于线程 2,调度器优先切换线程 1 执行;

  2. 线程 1 调用rt_mutex_take(&mutex_test, 100):互斥量未被持有,获取成功(标记为 “已持有”),执行LED_toggle(100)(10Hz 闪烁 6 次,耗时 600ms);

  3. 线程 2 进入就绪态,因优先级低,等待线程 1 释放 CPU。

3.2.3 优先级继承阶段(避免优先级反转)

  1. 线程 1 闪烁完成后,调用rt_mutex_release(&mutex_test):互斥量恢复 “未持有”,线程 1 执行rt_thread_delay(200)(主动阻塞,释放 CPU);

  2. 调度器切换线程 2 执行,线程 2 调用rt_mutex_take(&mutex_test, RT_WAITING_FOREVER):获取成功,执行LED_toggle(1000)(1Hz 闪烁,耗时 6000ms);

  3. 线程 1 的 200ms 延迟结束,进入就绪态:因优先级高,调度器尝试切换线程 1 执行,但互斥量已被线程 2 持有;

  4. 优先级继承生效:内核检测到 “高优先级线程 1 等待低优先级线程 2 的互斥量”,将线程 2 优先级临时提升至 4(与线程 1 一致),确保线程 2 不被其他线程抢占,加速资源释放。

3.2.4 资源释放与优先级恢复

  1. 线程 2 完成闪烁后,调用rt_mutex_release(&mutex_test):互斥量释放,唤醒阻塞的线程 1,内核自动将线程 2 优先级恢复为 5;

  2. 调度器切换线程 1 执行,线程 1 成功获取互斥量,继续 10Hz 闪烁;

  3. 线程 2 回到就绪态,等待下一次资源竞争。

3.3 同步效果

  1. 资源独占保障:LED 始终按单一频率闪烁(10Hz 或 1Hz),无频率混乱(如快速闪与慢速闪穿插),证明互斥量阻止了线程对 LED 的并发操作;

  2. 优先级继承效果:线程 2 持有资源时,不会被其他线程抢占,6000ms 内可快速释放资源,避免线程 1 长期阻塞;

  3. 超时机制作用:线程 1 的 100ms 超时获取,若互斥量因异常无法释放,线程 1 会放弃请求,避免自身永久阻塞(提升系统鲁棒性)。

四、关键设计注意事项

  1. 禁止中断中使用:互斥量的take/release可能导致线程阻塞,而中断服务函数(ISR)不允许阻塞,因此绝不能在 ISR 中调用互斥量 API;

  2. 成对调用 take/release:获取互斥量后必须释放,避免 “死锁”(如线程 1 获取后未释放就阻塞,其他线程无法获取);

  3. 避免递归获取:同一线程不能多次调用rt_mutex_take获取同一互斥量(会导致线程自身阻塞),若需递归访问,需使用 RT-Thread 递归互斥量(rt_recursive_mutex);

  4. 初始化时机:静态初始化的互斥量必须在线程启动前完成(如示例中先初始化mutex_test,再创建线程),避免线程竞争未初始化的资源;

  5. 动态创建的内存管理:动态创建的互斥量(rt_mutex_create)使用完后,需调用rt_mutex_delete销毁,释放堆内存,避免内存泄漏。

总结

本文详细解析了 RT-Thread 互斥量的核心特性、API 函数与实操示例,重点说明了 “优先级继承” 如何解决优先级反转问题。通过静态初始化互斥量、多线程竞争 LED 资源的案例,验证了互斥量在共享资源独占访问中的有效性。实际开发中,需结合场景选择动态 / 静态方式,遵循 “成对调用、禁止中断使用” 等规则,确保系统稳定运行。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/947164.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

20232419 2025-2026-1 《网络与系统攻防技术》实验三实验报告

一、实验内容 1.1 了解恶意软件检测机制,学习免杀原理 1.2 熟悉msfvenom的使用,使用msfvenom中的编码器并尝试生成多种类型的文件 1.3 学习使用veil工具的使用 1.4 尝试进行压缩加壳和加密加壳 1.5 利用C语言shellco…

语义文本理解 BERT - MKT

语义文本理解 BERT 问题 如何区分一个同名的语义名字和物体? A区左边路口的房子 B区右边红绿灯的房子 两个房子含义是不一样的。 从“是什么”升级到“是什么以及在什么情境下”​​,提高了准确性。好的,这个问题…

详细介绍:分布式任务事务框架设计与实现方案

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

FM-Fusion 利用rgbd相机 ram-GroundingDINO-sam 重建语义地图 - MKT

FM-Fusion 利用rgbd相机 ram-GroundingDINO-sam 重建语义地图https://arxiv.org/pdf/2402.04555

AI元人文构想系列:从战略能力到价值对话的文明之路

AI元人文构想系列:从战略能力到价值对话的文明之路引言:超越“鹦鹉”与“黑洞”的AI未来 当前人工智能的发展正站在一个十字路口:一边是精于模仿却回避价值矛盾的“鹦鹉AI”,另一边是潜藏于金融、信息和地缘政治领…

Rig 项目深度分析报告

Rig 项目深度分析报告 基于我对这个项目的深入研究,让我为你详细分析 Rig 这个 Rust LLM 框架。 📋 项目概述 Rig 是由 Playgrounds 开发的开源 Rust 库,专门用于构建可扩展、模块化且符合人体工程学的 LLM 驱动应…

事件日志查看Windows安装软件情况

在事件日志中选择应用程序日志筛选事件来源,事件ID号

RT-Thread之创建线程

使用RT-Thread创建线程的一些代码模板。一、静态线程创建 1、thread_task.c文件 #include "thread_task.h" #include "main.h" #include <stdio.h> #include "rtthread.h"/…

cias_voice_plyer_handle.c 解析

#if VOICE_PLAY_BY_UART /**************** * 播报器参数初始化 * * * * **/ void audio_player_param_init() {outside_init_stream(&mp3_player, &mp3_player_end, IOT_AUDIO_PLAY_BUF_SIZE);if (!mp3_…

VirtualBox共享文件夹完全指南:实现Windows与Ubuntu无缝文件共享

VirtualBox共享文件夹完全指南:实现Windows与Ubuntu无缝文件共享 问题背景 在使用VirtualBox运行Ubuntu虚拟机时,经常需要在宿主机(Windows)和虚拟机(Ubuntu)之间传输文件。虽然可以通过USB设备或网络传输,但设…

凭借Ubuntu和i.MX 6ULL开发板构建网络共享

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

WampServer下载安装教程(附安装包,图文并茂) - 指南

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

【CI130x 离在线】FreeRTOS的流缓冲(StreamBuffer)

FreeRTOS StreamBuffer 详解 概述 StreamBuffer(流缓冲区)是 FreeRTOS 提供的一种轻量级数据流传输机制,用于在任务间或中断与任务间高效传输字节流数据。 基本特性 1. 数据结构字节流存储: 以 FIFO 方式存储字节数…

RT-Thread Nano源码浅析

了解RT-Thread Nano源码构成。一、了解源码 从官方下载到RT-Thread Nano源码目录如下图所示1、bsp文件夹 bsp文件夹里面存放的是板级支持包(board support package),用于存放RT-Thread为各种半导体厂商的评估板写好…

《从 “被动听” 到 “主动学”:课堂听讲助力大学生思维成长》

阅读完三篇文章,我首先想到的就是大一到大二的学习状态变化,突然发现上学期的我可能是刚高考完的原因,脑海中还深深印刻着高中生上课必须要认真听讲的烙印。虽然上课给带手机,但是一看到老师走进教室,总是下意识地…

用AI批量生成产品视频!Python+Google Veo 3.1 API让电商转化率飙升

今天跟大家分享一个超实用的电商运营技巧:如何用Python和Google Veo 3.1 AI,把枯燥的产品图片批量变成生动的营销视频。用户原创内容(UGC)现在特别火,对销售的拉动效果非常明显,有了这个工具,你也能轻松制作大量…

关于SQLite - 世界上装机量最多的数据库

关于SQLite - 世界上装机量最多的数据库? 使用C语言开发,使得它小巧精致而高效,直接采用偏底层的语言,使用文件的逻辑,实现SQL数据库的逻辑; 使用方:包括但不限于 Python、Java、C# 等; 无服务器的,…

模拟IIC与硬件IIIC哪个更常用?

在实际项目中,两种方式都被广泛使用,但模拟IIC(软件IIC)的应用场景更多、更普遍。 下面我们来详细解释一下两者的区别、优缺点和适用场景。 硬件IIC 硬件IIC是指由微控制器内部的专用IIC外设电路来实现的。你只需要…

251019 NOIP 模拟赛 T2 | dp 及其优化、调整法最优解性质、数形结合

OJ 传送门 原题: QOJ 5500 题意 有 \(n\) 个屋子排成一列,每个屋子里一个人,每个屋子可以开酒吧。 每个人会去自己左右两侧最近的(分别)酒吧消费。 一个方案的价值为 \(\sum _ {酒吧} 来这个酒吧的人数 \times p_i…