C++死鎖問題定位與分析
C++程序異常一般有兩種表現形式:程序崩潰和程序無響應。程序崩潰主要是由指針,數組越界等原因引起,這種情況可以直接通過在程序中加入Dump捕捉邏輯,分析dump文件,定位出崩潰的代碼。程序無響應主要是由死循環和死鎖兩個原因造成的,死循環我們可以通過查看CPU使用情況來初步判斷,然后轉存為Dump進行分析;死鎖問題是C++中最難定位和分析的一種程序異常問題,下面我們主要介紹下死鎖問題的一般定位和分析方法。
一,測試代碼
在介紹死鎖分析方法之前,先看一下本文中我們使用的測試代碼:
CRITICAL_SECTION cs1;
CRITICAL_SECTION cs2;
DWORD WINAPI Thread1(LPVOID lpParameter);
DWORD WINAPI Thread2(LPVOID lpParameter);
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
//初始化關鍵代碼段
InitializeCriticalSection(&cs1);
InitializeCriticalSection(&cs2);
//創建線程
HANDLE hThread1 = CreateThread(NULL, 0, Thread1, NULL, 0, NULL);
HANDLE hThread2 = CreateThread(NULL, 0, Thread2, NULL, 0, NULL);
//等待線程結束
WaitForSingleObject(hThread1, INFINITE);
WaitForSingleObject(hThread2, INFINITE);
Sleep(2000);
//關閉線程句柄
CloseHandle(hThread1);
CloseHandle(hThread2);
//釋放關鍵代碼段
DeleteCriticalSection(&cs1);
DeleteCriticalSection(&cs2);
return 0;
}
DWORD WINAPI Thread1(LPVOID lpParameter)
{
for (int i = 0; i < 10; i++)
{
EnterCriticalSection(&cs1);
Sleep(500);
EnterCriticalSection(&cs2);
std::cout << "Thread1" << std::endl;
LeaveCriticalSection(&cs2);
LeaveCriticalSection(&cs1);
}
return 1;
}
DWORD WINAPI Thread2(LPVOID lpParameter)
{
for (int i = 0; i < 10; i++)
{
EnterCriticalSection(&cs2);
Sleep(500);
EnterCriticalSection(&cs1);
std::cout << "Thread2" << std::endl;
LeaveCriticalSection(&cs1);
LeaveCriticalSection(&cs2);
}
return 1;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
在這個例子中,Thread1等待Thread2,Thread2等待Thread1,同時主線程等待着Thread1和Thread2,三個線程陷入死鎖狀態。
二,等待鏈
等待鏈是線程和同步對象的交替序列;每個線程都在等待它后面的對象,該對象由鏈中的后續線程擁有。通過分析等待鏈,我們可以清晰地看到各個子線程/子進程掛起所在等待的下一個線程/進程。(等待鏈更多知識請轉至Wait Chain Traversal)
在《Windows核心編程》一書附帶的源碼09-LockCop中,詳細介紹了WCT的編碼實現,感興趣的可以去閱讀一下源碼,附上下載鏈接(Windows核心編程(第5版中文版) 源碼)。編譯09-LockCop,我們可以得到09-LockCop.exe應用程序,打開改程序,選擇我們的進程DeadLockTest.exe,分析如下:
線程8248等待線程9524,9524在等待6432,而6432又在等待9524,陷入了互相等待的處境中,導致死鎖。因此可以確認DeadLockTest.exe的無響應是由死鎖造成的。
我們也可以直接通過windows的任務管理器來檢測一個程序是否陷入死鎖,打開任務管理器-性能-資源監視器,選中我們的測試程序,右鍵菜單“分析等待鏈”:
分析結果如下:
可以看到9524線程與6432線程陷入了互相等待鎖資源的處境,程序死鎖。
三,死鎖的定位和分析
如果死鎖的程序剛好在我們自己的開發機器上,那么使用WinDbg的Attach To A Proccess功能將死鎖程序直接附加到WinDbg中進行分析;如果不在我們的機器上,可以通過Windows的資源管理器對進行進行創建轉儲文件轉換為dump(注意32位和64位的區別),再通過WinDbg對dump進行分析。為了方便,我們直接以Attach方式講解。
Attach程序后,輸入!locks命令查看鎖的狀態:
總共有兩個鎖cs1(012e4394)和cs2(012e437c),這兩個鎖的LockCount(表示還有多少個線程在等待這個臨界區)都為1,說明這兩個鎖都處於等待狀態。
再輸入~*kb查看一下當前各個線程的堆棧:
線程2534和線程1920都處於等待狀態,線程2534等待鎖012e437c,線程1920等待鎖012e4394。再結合上面的鎖信息可以知道,鎖012e4394被線程2534擁有,鎖012e437c被線程1920擁有,所以這兩個線程陷入了互相等待的死鎖中。等待鎖的源碼也直接定位到了,問題解決。
本文舉例比較簡單,主要是介紹死鎖問題的定位以及分析的一般方法,具體問題還要視情況具體分析。
from:https://blog.csdn.net/bajianxiaofendui/article/details/86983550