一、 概述
C++多線程開發中,容易出現死鎖導致程序掛起的現象。
解決步驟分為三步:
1、檢測死鎖線程。
2、打印線程信息。
3、修改死鎖程序。
二、 程序示例
VS2005創建支持MFC的win32控制台程序。
代碼見示例代碼DeadLockTest.cpp。
[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檢測工具。
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以及關鍵代碼段進入信息。
[cpp]
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對關鍵代碼段的進入與退出順序改為相同。程序運行正常。
修改後線程代碼。
[cpp]
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;
}
作者:segen_jaa