接續上一回死鎖理論分析,本文將成因一:同一線程重複加鎖 轉化為真實 C 代碼(基於 pthread)演示,並展示如何使用 pthread_mutex_trylock 避免死鎖,實現程序正常運行。
在線程函數中對同一互斥量連續調用兩次 pthread_mutex_lock:
void *thread_func1(void *param) {
int i = 0, res = 0;
pthread_t tid = pthread_self();
printf("Thread ID::%u -----------S----------\n", (unsigned)tid);
while (i++ < 4) {
#if MUTEX_ENABLE
res = pthread_mutex_lock(&mutex_value); // 第一次加鎖
if (res) printf(" mutex lock failed\n");
#endif
read_data("Thread_1");
// 新增:對同一互斥量第二次加鎖 → 死鎖
#if MUTEX_ENABLE
res = pthread_mutex_lock(&mutex_value); // 第二次加鎖
if (res) printf(" mutex lock failed\n");
#endif
#if MUTEX_ENABLE
res = pthread_mutex_unlock(&mutex_value);
if (res) printf(" mutex unlock failed\n");
#endif
sleep(2);
}
printf("Thread ID::%u -----------E----------\n", (unsigned)tid);
pthread_exit(&status);
}
Create first thread Create second thread Thread ID::3076418368 -----------S---------- [Thread_1] start reading data Thread ID::3068025664 -----------S---------- [Thread_1] data = 0 [Thread_1] end reading data mutex can't be destroyed // 程序卡死,無法銷毀互斥量
線程 1 在第二次 lock 處永久阻塞,線程 2 也無法運行,主線程卡在 pthread_mutex_destroy,程序異常終止。
將第二次 pthread_mutex_lock 替換為 pthread_mutex_trylock:
// 嘗試加鎖,若已被鎖定(即使是自己),立即返回 EBUSY,不阻塞
res = pthread_mutex_trylock(&mutex_value);
if (res == EBUSY) {
printf(" mutex trylock failed (already locked)\n");
// 可選:記錄日志、跳過、重試等
} else if (res) {
printf(" mutex trylock failed (other error)\n");
}
Create first thread Create second thread Thread ID::3076127552 -----------S---------- [Thread_1] start reading data Thread ID::3067734848 -----------S---------- [Thread_1] data = 0 [Thread_1] end reading data mutex lock failed // trylock 失敗,但不阻塞! [Thread_2] start writing data [Thread_2] data = 1 [Thread_2] end writing data [Thread_1] start reading data [Thread_1] data = 1 [Thread_1] end reading data mutex lock failed ... 循環正常運行至結束 ... Thread ID::3076127552 -----------E---------- Thread ID::3067734848 -----------E----------
| 指標 | pthread_mutex_lock | pthread_mutex_trylock |
|---|---|---|
| 已鎖定時行為 | 阻塞等待 | 立即返回 EBUSY |
| 死鎖風險 | 高(自我死鎖) | 無(不阻塞) |
| 適用場景 | 確信無重複加鎖的正常保護 | 不確定鎖狀態、需非阻塞嘗試 |
| 返回值 | 0 成功,非 0 失敗 | 0 成功,EBUSY 忙,其他錯誤碼 |
lock,只有在「可能重複加鎖」或「需非阻塞檢查」時用 trylock。PTHREAD_MUTEX_RECURSIVE 更清晰。同一線程重複加鎖是最易發現、最易避免的死鎖。pthread_mutex_trylock 提供了一種「嘗試加鎖、失敗即返」的非阻塞機制,能有效打破自我死鎖。但根治之道仍在代碼規範:清晰的鎖邊界、避免在鎖內調用複雜函數、嚴格的 Code Review。下一回將演示更隱蔽的「雙線程交叉加鎖」死鎖實戰。