C++死锁解决心得

一、 概述
C++多线程开发中,容易出现死锁导致程序挂起的现象。
关于死锁的信息,见百度百科http://baike.baidu.com/view/121723.htm

解决步骤分为三步:
1、检测死锁线程。
2、打印线程信息。
3、修改死锁程序。

二、 程序示例
VS2005创建支持MFC的win32控制台程序。
代码见示例代码DeadLockTest.cpp。

[cpp] view plain copy

 
  1. // DeadLockTest.cpp : Defines the entry point for the console application.  
  2. //  
  3.   
  4. #include "stdafx.h"  
  5. #include "DeadLockTest.h"  
  6.   
  7. #ifdef _DEBUG  
  8. #define new DEBUG_NEW  
  9. #endif  
  10.   
  11.   
  12. // The one and only application object  
  13.   
  14. CWinApp theApp;  
  15.   
  16. using namespace std;  
  17.   
  18. CRITICAL_SECTION cs1;  
  19. CRITICAL_SECTION cs2;  
  20. CRITICAL_SECTION csprint;  
  21.   
  22. //初始化关键代码段  
  23. void InitMyCriticalSection();  
  24. //删除关键代码段  
  25. void DeleteMyCriticalSection();  
  26. //打印信息  
  27. void PrintString(const CString& strInfo);  
  28.   
  29. DWORD WINAPI Thread1(LPVOID lpParameter);  
  30. DWORD WINAPI Thread2(LPVOID lpParameter);  
  31.   
  32. int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])  
  33. {  
  34.     int nRetCode = 0;  
  35.   
  36.     // initialize MFC and print and error on failure  
  37.     if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))  
  38.     {  
  39.         // TODO: change error code to suit your needs  
  40.         _tprintf(_T("Fatal Error: MFC initialization failedn"));  
  41.         nRetCode = 1;  
  42.   
  43.         return nRetCode;  
  44.     }  
  45.   
  46.     //初始化关键代码段  
  47.     InitMyCriticalSection();  
  48.   
  49.     //创建线程  
  50.     HANDLE hThread1 = CreateThread(NULL, 0, Thread1, NULL, 0, NULL);  
  51.     HANDLE hThread2 = CreateThread(NULL, 0, Thread2, NULL, 0, NULL);  
  52.   
  53.     //等待线程结束  
  54.     WaitForSingleObject(hThread1, INFINITE);  
  55.     WaitForSingleObject(hThread2, INFINITE);  
  56.   
  57.     //关闭线程句柄  
  58.     CloseHandle(hThread1);  
  59.     CloseHandle(hThread2);  
  60.   
  61.     //释放关键代码段  
  62.     DeleteMyCriticalSection();  
  63.   
  64.     return nRetCode;  
  65. }  
  66.   
  67. void InitMyCriticalSection()  
  68. {  
  69.     InitializeCriticalSection(&cs1);  
  70.     InitializeCriticalSection(&cs2);  
  71.     InitializeCriticalSection(&csprint);  
  72. }  
  73.   
  74. void DeleteMyCriticalSection()  
  75. {  
  76.     DeleteCriticalSection(&cs1);  
  77.     DeleteCriticalSection(&cs2);  
  78.     DeleteCriticalSection(&csprint);  
  79. }  
  80.   
  81. DWORD WINAPI Thread1(LPVOID lpParameter)  
  82. {  
  83.     for (int i = 0; i < 5; i++)  
  84.     {  
  85.         EnterCriticalSection(&cs1);  
  86.         Sleep(500);  
  87.         EnterCriticalSection(&cs2);  
  88.   
  89.         PrintString(_T("Thread1"));  
  90.   
  91.         LeaveCriticalSection(&cs2);  
  92.         LeaveCriticalSection(&cs1);  
  93.     }  
  94.   
  95.     return 1;  
  96. }  
  97.   
  98. DWORD WINAPI Thread2(LPVOID lpParameter)  
  99. {  
  100.     for (int i = 0; i < 5; i++)  
  101.     {  
  102.         EnterCriticalSection(&cs2);  
  103.         Sleep(500);  
  104.         EnterCriticalSection(&cs1);  
  105.   
  106.         PrintString(_T("Thread2"));  
  107.   
  108.         LeaveCriticalSection(&cs1);  
  109.         LeaveCriticalSection(&cs2);  
  110.     }  
  111.   
  112.     return 1;  
  113. }  
  114.   
  115. void PrintString(const CString& strInfo)  
  116. {  
  117.     EnterCriticalSection(&csprint);  
  118.     wcout<<(const TCHAR*)strInfo<<endl;  
  119.     LeaveCriticalSection(&csprint);  
  120. }  

 

运行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,得到线程信息。

C++死锁解决心得

检测到程序挂起由死锁引起。

线程4014:等待线程772、线程4012完成。
线程772:拥有关键代码段A,等待关键代码段B(被线程4012拥有)。
线程4012:拥有关键代码段B,等待关键代码段A(被线程772拥有)。

线程772与4012互相等待,程序发生死锁现象。

四、 打印信息
为了便于查找问题,我们加上线程打印信息。
打印线程名称、线程ID以及关键代码段进入信息。

 

[cpp] view plain copy

 
    1. DWORD WINAPI Thread1(LPVOID lpParameter)  
    2. {  
    3.     CString strThreadID = _T("");  
    4.     strThreadID.Format(_T("%d"), GetCurrentThreadId());  
    5.   
    6.     CString strPrintInfo = _T("");  
    7.   
    8.     for (int i = 0; i < 5; i++)  
    9.     {  
    10.         EnterCriticalSection(&cs1);  
    11.   
    12.         strPrintInfo = _T("");  
    13.         strPrintInfo += _T("Thread1 ");  
    14.         strPrintInfo += strThreadID;  
    15.         strPrintInfo += _T(" EnterCriticalSection(&cs1)");  
    16.   
    17.         PrintString(strPrintInfo);  
    18.   
    19.         Sleep(500);  
    20.         EnterCriticalSection(&cs2);  
    21.   
    22.         strPrintInfo = _T("");  
    23.         strPrintInfo += _T("Thread1 ");  
    24.         strPrintInfo += strThreadID;  
    25.         strPrintInfo += _T(" EnterCriticalSection(&cs2)");  
    26.   
    27.         PrintString(strPrintInfo);  
    28.   
    29.         LeaveCriticalSection(&cs2);  
    30.         LeaveCriticalSection(&cs1);  
    31.     }  
    32.   
    33.     return 1;  
    34. }  
    35.   
    36. DWORD WINAPI Thread2(LPVOID lpParameter)  
    37. {  
    38.     CString strThreadID = _T("");  
    39.     strThreadID.Format(_T("%d"), GetCurrentThreadId());  
    40.   
    41.     CString strPrintInfo = _T("");  
    42.   
    43.     for (int i = 0; i < 5; i++)  
    44.     {  
    45.         EnterCriticalSection(&cs2);  
    46.   
    47.         strPrintInfo = _T("");  
    48.         strPrintInfo += _T("Thread2 ");  
    49.         strPrintInfo += strThreadID;  
    50.         strPrintInfo += _T(" EnterCriticalSection(&cs2)");  
    51.   
    52.         PrintString(strPrintInfo);  
    53.   
    54.         Sleep(500);  
    55.   
    56.         EnterCriticalSection(&cs1);  
    57.   
    58.         strPrintInfo = _T("");  
    59.         strPrintInfo += _T("Thread2 ");  
    60.         strPrintInfo += strThreadID;  
    61.         strPrintInfo += _T(" EnterCriticalSection(&cs1)");  
    62.   
    63.         PrintString(strPrintInfo);  
    64.   
    65.         LeaveCriticalSection(&cs1);  
    66.         LeaveCriticalSection(&cs2);  
    67.     }  
    68.   
    69.     return 1;  
    70. }  
    71. 运行结果如下。
    72. C++死锁解决心得
    73. 五、 死锁修改
      线程互斥进行修改,Thread1与Thread2对关键代码段的进入与退出顺序改为相同。程序运行正常。
      修改后线程代码。

       

      [cpp] view plain copy

       
      1. DWORD WINAPI Thread1(LPVOID lpParameter)  
      2. {  
      3.     for (int i = 0; i < 5; i++)  
      4.     {  
      5.         EnterCriticalSection(&cs1);  
      6.         Sleep(500);  
      7.         EnterCriticalSection(&cs2);  
      8.   
      9.         PrintString(_T("Thread1"));  
      10.   
      11.         LeaveCriticalSection(&cs2);  
      12.         LeaveCriticalSection(&cs1);  
      13.     }  
      14.   
      15.     return 1;  
      16. }  
      17.   
      18. DWORD WINAPI Thread2(LPVOID lpParameter)  
      19. {  
      20.     for (int i = 0; i < 5; i++)  
      21.     {  
      22.         EnterCriticalSection(&cs1);  
      23.         Sleep(500);  
      24.         EnterCriticalSection(&cs2);  
      25.   
      26.         PrintString(_T("Thread2"));  
      27.   
      28.         LeaveCriticalSection(&cs2);  
      29.         LeaveCriticalSection(&cs1);  
      30.     }  
      31.   
      32.     return 1;  
      33. }  

原文链接: https://www.cnblogs.com/helloboyang/p/5306423.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

    C++死锁解决心得

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/230699

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月13日 下午2:45
下一篇 2023年2月13日 下午2:45

相关推荐