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

C語言線程死鎖詳解(一):概念、兩大成因與偽代碼演示

     編輯:關於C

死鎖 是多線程/多進程編程中的經典災難:多個執行單元因爭奪資源而相互無限等待,導致系統整體停滯。本文通過生活類比、兩大核心成因分析、偽代碼演示,建立對死鎖的直觀認知,為後續實戰代碼排查打基礎。

生活類比:馬路癱瘓

將運行的程序比作行駛的汽車,共享資源比作馬路。兩輛車相撞停在路中,佔用馬路不放,後車無法通行,最終整條路癱瘓。這就是死鎖:佔有資源並等待其他資源,形成循環等待。

死鎖的兩大核心成因

成因一:同一線程重複加鎖(自我死鎖)

線程對已加鎖的互斥量再次加鎖。若互斥量非遞歸型,線程將阻塞在自己手中,永無解脫之日。

thread_func() {
    lock(mutex);      // 第一次加鎖
    // do something
    lock(mutex);      // 第二次加鎖 → 死鎖!自己等自己
    // do another
    unlock(mutex);
    unlock(mutex);
}

避免簡單:代碼審查易發現;或使用 pthread_mutex_trylock 非阻塞嘗試。

成因二:多線程交叉加鎖(循環等待)

線程 A 持有鎖 1 等鎖 2;線程 B 持有鎖 2 等鎖 1。雙方僵持,系統癱瘓。這是最隱蔽、最常見的死鎖,因分佈在不同線程、不同代碼文件中,極難靜態發現。

thread_funcA() {
    lock(mutex1);     // A 持有 1
    // do something
    lock(mutex2);     // A 等 2
    // do another
    unlock(mutex2);
    unlock(mutex1);
}

thread_funcB() {
    lock(mutex2);     // B 持有 2
    // do something
    lock(mutex1);     // B 等 1
    // do another
    unlock(mutex1);
    unlock(mutex2);
}

死鎖的四個必要條件(Coffman 條件)

條件說明破壞策略
互斥資源一次只能被一個線程佔有難以破壞(硬件/內核特性)
佔有並等待線程持有資源同時申請新資源一次性申請所有資源
不可搶佔資源只能由持有者主動釋放允許搶佔/超時釋放
循環等待存在線程環,每個等待下一個持有的資源統一資源編號,按序申請

破壞任一條件即可避免死鎖,工程上最常用的是破壞「循環等待」(統一加鎖順序)。

死鎖的後果

  • 程序無限阻塞,消耗系統資源(線程棧、內核對象)。
  • 無法正常退出,資源洩漏。
  • 嚴重時導致進程假死,需強制殺掉。

總結

死鎖雖可怕,但成因清晰、規律可循:

  1. 同一線程重複加鎖:代碼規範+trylock 易避免。
  2. 多線程交叉加鎖:嚴格執行「全局統一加鎖順序」是核心對策。

下一回將展示如何將偽代碼轉為真實 C 代碼演示死鎖,並介紹 pthread_mutex_trylock 避免自我死鎖的實戰技巧。

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