程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> C++死鎖解決心得

C++死鎖解決心得

編輯:C++入門知識

一、 概述
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

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved