C++死鎖解決心得
阿新 • • 發佈:2019-02-10
一、概述
C++多執行緒開發中,容易出現死鎖導致程式掛起的現象。
關於死鎖的資訊,見百度百科http://baike.baidu.com/view/121723.htm。
解決步驟分為三步:
1、檢測死鎖執行緒。
2、列印執行緒資訊。
3、修改死鎖程式。
二、程式示例
VS2005建立支援MFC的win32控制檯程式。
程式碼見示例程式碼DeadLockTest.cpp。
執行結果如下。
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;
}