一、 概述
C++多線程開發中,容易出現死鎖導致程序掛起的現象。
關於死鎖的信息,見百度百科http://baike.baidu.com/view/121723.htm。
解決步驟分為三步:
1、檢測死鎖線程。
2、打印線程信息。
3、修改死鎖程序。
二、 程序示例
VS2005創建支持MFC的win32控制台程序。
代碼見示例代碼DeadLockTest.cpp。
- // DeadLockTest.cpp : Defines the entry point for the console application.
- //
- #include "stdafx.h"
- #include "DeadLockTest.h"
- #ifdef _DEBUG
- #define new DEBUG_NEW
- #endif
- // The one and only application object
- CWinApp theApp;
- using namespace std;
- CRITICAL_SECTION cs1;
- CRITICAL_SECTION cs2;
- CRITICAL_SECTION csprint;
- //初始化關鍵代碼段
- void InitMyCriticalSection();
- //刪除關鍵代碼段
- void DeleteMyCriticalSection();
- //打印信息
- void PrintString(const CString& strInfo);
- DWORD WINAPI Thread1(LPVOID lpParameter);
- DWORD WINAPI Thread2(LPVOID lpParameter);
- int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
- {
- int nRetCode = 0;
- // initialize MFC and print and error on failure
- if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
- {
- // TODO: change error code to suit your needs
- _tprintf(_T("Fatal Error: MFC initialization failed\n"));
- nRetCode = 1;
- return nRetCode;
- }
- //初始化關鍵代碼段
- InitMyCriticalSection();
- //創建線程
- HANDLE hThread1 = CreateThread(NULL, 0, Thread1, NULL, 0, NULL);
- HANDLE hThread2 = CreateThread(NULL, 0, Thread2, NULL, 0, NULL);
- //等待線程結束
- WaitForSingleObject(hThread1, INFINITE);
- WaitForSingleObject(hThread2, INFINITE);
- //關閉線程句柄
- CloseHandle(hThread1);
- CloseHandle(hThread2);
- //釋放關鍵代碼段
- DeleteMyCriticalSection();
- return nRetCode;
- }
- void InitMyCriticalSection()
- {
- InitializeCriticalSection(&cs1);
- InitializeCriticalSection(&cs2);
- InitializeCriticalSection(&csprint);
- }
- void DeleteMyCriticalSection()
- {
- DeleteCriticalSection(&cs1);
- DeleteCriticalSection(&cs2);
- DeleteCriticalSection(&csprint);
- }
- DWORD WINAPI Thread1(LPVOID lpParameter)
- {
- for (int i = 0; i < 5; i++)
- {
- EnterCriticalSection(&cs1);
- Sleep(500);
- EnterCriticalSection(&cs2);
- PrintString(_T("Thread1"));
- LeaveCriticalSection(&cs2);
- LeaveCriticalSection(&cs1);
- }
- return 1;
- }
- DWORD WINAPI Thread2(LPVOID lpParameter)
- {
- for (int i = 0; i < 5; i++)
- {
- EnterCriticalSection(&cs2);
- Sleep(500);
- EnterCriticalSection(&cs1);
- PrintString(_T("Thread2"));
- LeaveCriticalSection(&cs1);
- LeaveCriticalSection(&cs2);
- }
- return 1;
- }
- void PrintString(const CString& strInfo)
- {
- EnterCriticalSection(&csprint);
- wcout<<(const TCHAR*)strInfo<<endl;
- LeaveCriticalSection(&csprint);
- }
運行DeadLockTest.exe,程序掛起。
三、 死鎖檢測
檢測工具見《Windows核心編程》,第9章9.8.6節LockCop檢測工具。
工具源碼地址:http://www1.wintellect.com/Resources/Details/86。
LockCop可使用vs2010編譯成功。
備注:該工具使用了Windows Vista/ 7提供的WCT API,故需要在Windows Vista/ 7系統運行LockCop檢測工具。
檢測,掛起的DeadLockTest.exe,得到線程信息。

檢測到程序掛起由死鎖引起。
線程4014:等待線程772、線程4012完成。
線程772:擁有關鍵代碼段A,等待關鍵代碼段B(被線程4012擁有)。
線程4012:擁有關鍵代碼段B,等待關鍵代碼段A(被線程772擁有)。
線程772與4012互相等待,程序發生死鎖現象。
四、 打印信息
為了便於查找問題,我們加上線程打印信息。
打印線程名稱、線程ID以及關鍵代碼段進入信息。
- DWORD WINAPI Thread1(LPVOID lpParameter)
- {
- CString strThreadID = _T("");
- strThreadID.Format(_T("%d"), GetCurrentThreadId());
- CString strPrintInfo = _T("");
- for (int i = 0; i < 5; i++)
- {
- EnterCriticalSection(&cs1);
- strPrintInfo = _T("");
- strPrintInfo += _T("Thread1 ");
- strPrintInfo += strThreadID;
- strPrintInfo += _T(" EnterCriticalSection(&cs1)");
- PrintString(strPrintInfo);
- Sleep(500);
- EnterCriticalSection(&cs2);
- strPrintInfo = _T("");
- strPrintInfo += _T("Thread1 ");
- strPrintInfo += strThreadID;
- strPrintInfo += _T(" EnterCriticalSection(&cs2)");
- PrintString(strPrintInfo);
- LeaveCriticalSection(&cs2);
- LeaveCriticalSection(&cs1);
- }
- return 1;
- }
- DWORD WINAPI Thread2(LPVOID lpParameter)
- {
- CString strThreadID = _T("");
- strThreadID.Format(_T("%d"), GetCurrentThreadId());
- CString strPrintInfo = _T("");
- for (int i = 0; i < 5; i++)
- {
- EnterCriticalSection(&cs2);
- strPrintInfo = _T("");
- strPrintInfo += _T("Thread2 ");
- strPrintInfo += strThreadID;
- strPrintInfo += _T(" EnterCriticalSection(&cs2)");
- PrintString(strPrintInfo);
- Sleep(500);
- EnterCriticalSection(&cs1);
- strPrintInfo = _T("");
- strPrintInfo += _T("Thread2 ");
- strPrintInfo += strThreadID;
- strPrintInfo += _T(" EnterCriticalSection(&cs1)");
- PrintString(strPrintInfo);
- LeaveCriticalSection(&cs1);
- LeaveCriticalSection(&cs2);
- }
- return 1;
- }
- 運行結果如下。

-
五、 死鎖修改
線程互斥進行修改,Thread1與Thread2對關鍵代碼段的進入與退出順序改為相同。程序運行正常。
修改后線程代碼。- DWORD WINAPI Thread1(LPVOID lpParameter)
- {
- for (int i = 0; i < 5; i++)
- {
- EnterCriticalSection(&cs1);
- Sleep(500);
- EnterCriticalSection(&cs2);
- PrintString(_T("Thread1"));
- LeaveCriticalSection(&cs2);
- LeaveCriticalSection(&cs1);
- }
- return 1;
- }
- DWORD WINAPI Thread2(LPVOID lpParameter)
- {
- for (int i = 0; i < 5; i++)
- {
- EnterCriticalSection(&cs1);
- Sleep(500);
- EnterCriticalSection(&cs2);
- PrintString(_T("Thread2"));
- LeaveCriticalSection(&cs2);
- LeaveCriticalSection(&cs1);
- }
- return 1;
- }
