C++死锁解决心得

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

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

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

[cpp] view plaincopy

  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 failed\n"));  
  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,得到线程信息。

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

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

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

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

[cpp] view plaincopy

  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. }  

运行结果如下。

五、 死锁修改
线程互斥进行修改,Thread1与Thread2对关键代码段的进入与退出顺序改为相同。程序运行正常。
修改后线程代码。

[cpp] view plaincopy

  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. }  
时间: 2024-09-10 00:51:02

C++死锁解决心得的相关文章

SqlServer定时备份数据库和定时杀死数据库死锁解决

原文:SqlServer定时备份数据库和定时杀死数据库死锁解决 上周五组长更我说了一句要杀死数据库的死锁进程,因为自己对数据库不是很熟悉,突然组长说了我也就决定一定要倒腾一下,不然自己怎么提高呢?现在不研究,说不定下次还是要研究呢,倒腾出来了就可以在下次用到了,后来组长又补了一句:"还有定是备份数据库的问题要解决",说干就干. PS:Sqlserver 2008 R2,windows 8 64位 1.备份数据库  因为要备份,我们就要用到Sqlserver的代理,默认数据库的代理是不开

Oracle死锁解决方法

select p.spid,c.object_name,b.session_id,b.oracle_username,b.os_user_name from v$process p,v$session a, v$locked_object b,all_objects c where p.addr=a.paddr and a.process=b.process and c.object_id=b.object_id 能查询到死锁的表名 SELECT s.username,l.OBJECT_ID,l

Zblog有关数据误删与后台无法登陆的实战解决心得

中介交易 http://www.aliyun.com/zixun/aggregation/6858.html">SEO诊断 淘宝客 云主机 技术大厅 今天是8月25日奋战了一个晚上,我的Z-blog终于恢复正常了,所担心的数据会丢失的心放下,在此非常感谢推一把月章老师的指点,现将此次事件做一个记录总结,希望能帮助能为需要同此需求的朋友们做一个价值参考. 先我说说我的Z-blog是怎么一回事吧?一天中午,突然猛然想起要把年前的个人博客网站进行重新定位,想把自己的网站打造成一个有关陶瓷企业网络

js原生appendChild的bug解决心得分享

appendChild 主要是用来追加节点 插入到最后 复制代码 代码如下: window.onload = function(){ var ul2 = document.getElementById('ul2'); var oli = document.getElementsByTagName('li'); for(var i=0;i<oli.length;i++){ ul2.appendChild(oli[i]); } } <h3>讲Id为ul1的内容插入到ul2里面</h3&

js原生appendChild的bug解决心得分享_javascript技巧

appendChild 主要是用来追加节点 插入到最后 复制代码 代码如下: window.onload = function(){ var ul2 = document.getElementById('ul2'); var oli = document.getElementsByTagName('li'); for(var i=0;i<oli.length;i++){ ul2.appendChild(oli[i]); } } <h3>讲Id为ul1的内容插入到ul2里面</h3&

sql insert into .. select..死锁解决办法

方法一 insert into a select * from b with(xlock) where form_no=@form_no 方法二,存储过程 sp_who --sql2000及以上 dbcc inputbuffer(spid)--用于查看具体的语句 kill spid --杀掉进程. --还是 select * from sys.sysprocesses --sql2005及以上 a. 按平均 cpu 时间排在前五个的查询 此方案提供有关 cpu 时间.io 读写以及按平均 cpu

SQL Server 死锁处理和优化心得

前段时间提到的"sql server 2005 死锁解决探索",死锁严重,平均每天会发生一次死锁,在解决和处理SQL server2005死锁中查了很多资料和想了很多办法, 对为何出现死锁和怎样较少死锁有了进一步认识,在这里和大家一起分享:           sql server 锁类型    在数据库中主要存在两种锁: S(共享锁)和X(排他锁)       S(共享锁):在执行查询数据时,sql server会将行锁定,这时只能查询数据,删,改被阻塞,       X(排他锁):

用TRY/CATCH解决sql server2005的死锁

让我们从这样一个示例开始说起,它在 SQL Server 2000 和 2005 中都能引起死锁.在本文中,我使用 SQL Server 2005 的最新 CTP(社区技术预览,Community Technology Preview)版本,SQL Server 2005 Beta 2(7 月发布)也同样适用.如果您没有 Beta 2 或最新的 CTP 版本,请下载 SQL Server 2005 Express 的最新版本,用它来进行试验. 可能发生的死锁情况有很多,[参阅http://msd

MySQL死锁问题分析及解决方法实例详解_Mysql

MySQL死锁问题是很多程序员在项目开发中常遇到的问题,现就MySQL死锁及解决方法详解如下: 1.MySQL常用存储引擎的锁机制 MyISAM和MEMORY采用表级锁(table-level locking) BDB采用页面锁(page-level locking)或表级锁,默认为页面锁 InnoDB支持行级锁(row-level locking)和表级锁,默认为行级锁 2.各种锁特点 表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低 行级锁:开销大,加锁慢;