死鎖是多線程編程的噩夢。本文通過一個最小化 C/pthread 例子,演示 同一線程對同一互斥量重複加鎖 導致的自我死鎖,並展示 pthread_mutex_trylock 非阻塞檢測機制避免死鎖的方法。
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void* thread_func1(void* arg) {
for (int i = 0; i < 4; i++) {
pthread_mutex_lock(&mutex); // 第一次加鎖
read_data("Thread_1"); // 讀取共享數據
pthread_mutex_lock(&mutex); // 第二次加鎖 → 此處永久阻塞!
pthread_mutex_unlock(&mutex);
sleep(2);
}
return NULL;
}
運行輸出顯示:Thread_1 讀取數據後,第二次 lock 卡死,主線程無法銷毀互斥量,報 mutex can't be destroyed。
PTHREAD_MUTEX_DEFAULT 不支持遞歸加鎖。lock 時,發現鎖已被自己持有,但無法區分是否遞歸,只能無限等待自己釋放 → 死等。int res = pthread_mutex_trylock(&mutex);
if (res == 0) {
// 成功獲取鎖
pthread_mutex_unlock(&mutex);
} else if (res == EBUSY) {
// 鎖已被佔用(含自己持有),不阻塞,繼續執行
printf("mutex lock failed\n");
}
修改後運行輸出:第二次加鎖返回 EBUSY,線程繼續執行,讀寫線程交替正常運行至結束,無死鎖。
另一種方案是初始化為遞歸類型:
pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&mutex, &attr);
此時同一線程可多次 lock,需對應次數 unlock。但遞歸鎖有性能開銷,掩蓋設計缺陷,不推薦常規使用。
lock_guard、C 宏 LOCK/UNLOCK),確保異常路徑也能解鎖。helgrind (Valgrind)、ThreadSanitizer (Clang/GCC) 可自動檢測潛在死鎖。死鎖往往源於「鎖的持有範圍過大」或「調用鏈不清晰導致重複加鎖」。trylock 提供非阻塞探測能力,是避免自我死鎖的實用工具;但根治之道在於清晰的鎖層級設計與代碼審查。