首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C >> 關於C

C語言線程死鎖實戰(一):同一線程重複加鎖死鎖與 trylock 避免方案

     編輯:關於C

接續上一回死鎖理論分析,本文將成因一:同一線程重複加鎖 轉化為真實 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_trylock 避免死鎖

將第二次 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");
}

trylock 版運行結果

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_lockpthread_mutex_trylock
已鎖定時行為阻塞等待立即返回 EBUSY
死鎖風險高(自我死鎖)無(不阻塞)
適用場景確信無重複加鎖的正常保護不確定鎖狀態、需非阻塞嘗試
返回值0 成功,非 0 失敗0 成功,EBUSY 忙,其他錯誤碼

trylock 使用建議

  1. 不要濫用:正常邏輯用 lock,只有在「可能重複加鎖」或「需非阻塞檢查」時用 trylock。
  2. 處理 EBUSY:捕獲後應有明確策略(跳過、重試、上報),避免忽略導致邏輯漏洞。
  3. 遞歸互斥量替代:若確需同一線程多次加鎖,初始化屬性設置 PTHREAD_MUTEX_RECURSIVE 更清晰。

總結

同一線程重複加鎖是最易發現、最易避免的死鎖。pthread_mutex_trylock 提供了一種「嘗試加鎖、失敗即返」的非阻塞機制,能有效打破自我死鎖。但根治之道仍在代碼規範:清晰的鎖邊界、避免在鎖內調用複雜函數、嚴格的 Code Review。下一回將演示更隱蔽的「雙線程交叉加鎖」死鎖實戰。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved