RT-Thread 之互斥量使用
一、互斥量概述
互斥量(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 初始階段(資源初始化)
-
調用ThreadStart時,先關閉中斷,避免互斥量初始化被打斷(原子操作);
-
靜態初始化互斥量mutex_test,等待隊列按 FIFO 排序,初始狀態為 “未被持有”;
-
動態創建線程 1(優先級 4)和線程 2(優先級 5),創建成功后啟動線程;
-
恢復中斷,RT-Thread 調度器開始工作,優先調度高優先級線程。
3.2.2 競爭階段(高優先級線程優先)
-
線程 1 優先級高于線程 2,調度器優先切換線程 1 執行;
-
線程 1 調用rt_mutex_take(&mutex_test, 100):互斥量未被持有,獲取成功(標記為 “已持有”),執行LED_toggle(100)(10Hz 閃爍 6 次,耗時 600ms);
-
線程 2 進入就緒態,因優先級低,等待線程 1 釋放 CPU。
3.2.3 優先級繼承階段(避免優先級反轉)
-
線程 1 閃爍完成后,調用rt_mutex_release(&mutex_test):互斥量恢復 “未持有”,線程 1 執行rt_thread_delay(200)(主動阻塞,釋放 CPU);
-
調度器切換線程 2 執行,線程 2 調用rt_mutex_take(&mutex_test, RT_WAITING_FOREVER):獲取成功,執行LED_toggle(1000)(1Hz 閃爍,耗時 6000ms);
-
線程 1 的 200ms 延遲結束,進入就緒態:因優先級高,調度器嘗試切換線程 1 執行,但互斥量已被線程 2 持有;
-
優先級繼承生效:內核檢測到 “高優先級線程 1 等待低優先級線程 2 的互斥量”,將線程 2 優先級臨時提升至 4(與線程 1 一致),確保線程 2 不被其他線程搶占,加速資源釋放。
3.2.4 資源釋放與優先級恢復
-
線程 2 完成閃爍后,調用rt_mutex_release(&mutex_test):互斥量釋放,喚醒阻塞的線程 1,內核自動將線程 2 優先級恢復為 5;
-
調度器切換線程 1 執行,線程 1 成功獲取互斥量,繼續 10Hz 閃爍;
-
線程 2 回到就緒態,等待下一次資源競爭。
3.3 同步效果
-
資源獨占保障:LED 始終按單一頻率閃爍(10Hz 或 1Hz),無頻率混亂(如快速閃與慢速閃穿插),證明互斥量阻止了線程對 LED 的并發操作;
-
優先級繼承效果:線程 2 持有資源時,不會被其他線程搶占,6000ms 內可快速釋放資源,避免線程 1 長期阻塞;
-
超時機制作用:線程 1 的 100ms 超時獲取,若互斥量因異常無法釋放,線程 1 會放棄請求,避免自身永久阻塞(提升系統魯棒性)。
四、關鍵設計注意事項
-
禁止中斷中使用:互斥量的take/release可能導致線程阻塞,而中斷服務函數(ISR)不允許阻塞,因此絕不能在 ISR 中調用互斥量 API;
-
成對調用 take/release:獲取互斥量后必須釋放,避免 “死鎖”(如線程 1 獲取后未釋放就阻塞,其他線程無法獲取);
-
避免遞歸獲取:同一線程不能多次調用rt_mutex_take獲取同一互斥量(會導致線程自身阻塞),若需遞歸訪問,需使用 RT-Thread 遞歸互斥量(rt_recursive_mutex);
-
初始化時機:靜態初始化的互斥量必須在線程啟動前完成(如示例中先初始化mutex_test,再創建線程),避免線程競爭未初始化的資源;
-
動態創建的內存管理:動態創建的互斥量(rt_mutex_create)使用完后,需調用rt_mutex_delete銷毀,釋放堆內存,避免內存泄漏。
總結
本文詳細解析了 RT-Thread 互斥量的核心特性、API 函數與實操示例,重點說明了 “優先級繼承” 如何解決優先級反轉問題。通過靜態初始化互斥量、多線程競爭 LED 資源的案例,驗證了互斥量在共享資源獨占訪問中的有效性。實際開發中,需結合場景選擇動態 / 靜態方式,遵循 “成對調用、禁止中斷使用” 等規則,確保系統穩定運行。

互斥量(Mutex)是 RT-Thread 中用于解決線程間共享資源獨占訪問的核心 IPC 機制,本文簡單介紹了互斥量的API函數和使用示例。
浙公網安備 33010602011771號