死锁现象:在训练的时候,点击“终止”按钮时不时会发生死锁。

检测工具:LockCop、TRACE宏、::GetCurrentThreadID函数。

检测手段:

总结起来就是——

  第一步:用LockCop找哪几个线程死锁起来了,因为什么对象死锁的(比如说那些用于线程同步的工具类或者某些要Block的函数等等)。比如我这里就是两个线程6616和5112因为CCriticalSection和SendMessage锁死的,见图3;具体来说,我的程序里用了一个CCriticalSection m_cs对象。一个UI线程(主线程),一个Worker线程(跑算法)。Worker线程要往UI的CConsoleDialog写数据,具体来说,就是Worker线程要往UI的CConsoleDialog的Edit Control通过SetDlgItemText写入CString。因为我可能在后面的修改中Worker线程可能有多个,所以我在这个代码段用m_cs对象给锁起来了,如图1。

  第二步:用TRACE宏和::GetCurrentThreadID函数结合第一步找出的死锁对象CCriticalSection和SendMessage,找到每个线程具体死锁的代码位置。具体来说,我把所有CCriticalSection::Lock的前面都加了TRACE和::GetCurrentThreadID,由于使用SendMessage的地方太多,就没有加。TRACE给出的结果如图4所示,主线程ID是6616,Worker线程ID是5112。

  通过TRACE的结果可以看出线程6616最后死锁在代码170行的位置,因为最后一次TRACE输出是在169行;同理可以看出,线程5112最后死锁在代码70行的位置。

  第三步:分析。这里就要具体问题具体分析了。

  就我这个问题而言,

  第一方面,主线程6616对Stop函数的调用是由于我的Dialog上的一个CButton的点击而触发的;显然点击CButton会对Dialog进行SendMessage,而SendMessage在结束之前是会向内核申请CConsoleDialog的锁的,这也是SendMessage会block的原因(显然Windows让SendMessage会block对线程安全是有好处的,否则UI界面在接收多个Worker线程的数据的时候就会乱套)。另外注意观察图2的代码,Stop里面有一截代码是用m_cs锁起来了的,主线程6616在申请m_cs锁的时候死锁了,这时候主线程6616持有对CConsoleDialog的内核锁。

  第二方面,显然Worker线程5112一定是死锁在图1的代码段中的,因为线程5112的最后一个TRACE就在那里面输出,因为我们已经知道主线程6616是死锁在申请m_cs的锁的时候,所以结合LockCop的检测结果我们知道线程5112一定是死锁在申请SendMessage内核锁的时候。观察图1,就知道,唯一可能调用SendMessage的位置就是SetDlgItemText,线程5112在调用SetDlgItemText的时候是持有m_cs的锁的,但是CConsoleDialog的内核锁已经被主线程6616持有。这样就发生了相互等待,死锁就产生了。

  最后我是把SetDlgItemText的调用不再放到m_cs锁的区域内就解决死锁问题了,打破了死锁的闭环。

  LockCop的基本使用见这篇博文:http://blog.csdn.net/segen_jaa/article/details/7843654

LockCop通过下载Windows核心编程的源码编译可以得到,我是在CSDN下载的:http://download.csdn.net/detail/fksec/4209539

图1 死锁代码1

图2 死锁代码2

图3 LockCop检测结果

图4 TRACE结果

  刚刚把前面说的死锁问题解决,测试了一下发现不止那一个死锁问题,打得一手好脸。通过同样的过程发现(我在所有可能block的地方前后都加了TRACE和::GetCurrentThreadID,也就是说除了m_cs.Lock以外,还有一个::WaitForSingleObject),发现还有一个死锁问题如下。

  如图2-3所示,主线程6860阻塞了,但是没说是因为m_cs也没说是因为SendMessage。我估摸了一下,猜测很可能就是因为::WaitForSingleObject。另外,工作线程6848因为SendMessage阻塞了。

  再通过加TRACE。运行,进一步查看TRACE结果,如图2-4所示。找到了主线程阻塞的位置如图2-2的174行,工作线程阻塞的位置如图2-1的79行。

  分析一下,一方面,主线程在174行阻塞的时候是持有CConsoleDialog的内核锁的(因为Stop是由SendMessage调用的,进一步来说,是按了CButton导致的主线程调用SendMessage),此时主线程阻塞在等待CEvent对象上,这个对象本来应该是由工作线程来Set的。

  另一方面,工作线程在79行阻塞是因为主线程持有了CConsoleDialog的内核锁。此时工作线程虽然持有CEvent,但因为阻塞在这里,所以没法去Set它。这下相互等待就发生了,从而导致了死锁。

  最后是把174-175行的代码放到了CConsoleDialog::~CConsoleDialog中才解决的。因为那两行代码主要是为了防止CConsoleDialog已经被销毁了工作线程还在往CConsoleDialog写数据导致的崩溃。所以说从功能上讲把那两行代码放到CConsoleDialog的析构函数中是没有问题的,同时也避免了调用::WaitForSingleObject的时候持有CConsoleDialog的SendMessage内核锁,就打破了死锁的闭环。

图2-1

图2-2

图2-3

图2-4

Windows下MFC程序利用LockCop解决死锁的更多相关文章

  1. Windows下mysql忘记密码的解决方法

    Windows下mysql忘记密码的解决方法 mysql5.0 http://www.jb51.net/article/21984.htm方法一: 1.在DOS窗口下输入 net stop mysql ...

  2. windows下git bash中文乱码解决办法

    一.解决办法1:(直接上图) 1.在git bash下,右键 出现下图,选择options: 2.选择“Text” 3.将“Character set”设置为  UTF-8 转:windows下git ...

  3. Windows下的程序及热键监视神器——Spy++

    Windows下的程序及热键监视神器--Spy++ 背景 在使用Windows的时候,偶尔会发现某些应用程序的热键不生效了:又或是桌面弹出了弹框却并不知道这个弹框来自何处.例如,本人最近使用Vim的时 ...

  4. windows下的句柄利用

    什么是句柄 维基百科:在程序设计中,句柄(handle)是Windows操作系统用来标识被应用程序所建立或使用的对象的整数.其本质相当于带有引用计数的智能指针.当一个应用程序要引用其他系统(如数据库. ...

  5. linux下QT程序输出乱码解决方法

    参考文章:http://blog.csdn.net/jiang1013nan/article/details/6667871 http://my.oschina.net/zjlaobusi/blog/ ...

  6. Windows下 Mysql启动报1067解决方法

    前几天刚入职安装了一下Mysql  刚开始能打开  今天去公司发现启动不了服务 报1067错误, 在网上查看了一些方法,好多种版本..以下是本人的解决方法 1.打开运行-事件查看器--Windows日 ...

  7. windows下控制台程序实现窗口显示

    windows下实现窗口显示,如果限定是C/C++语言,并且是原生Windows支持,需要使用GDI或GDI+.一般是在Visual Studio里新建Win32应用程序,而不是Win32 conso ...

  8. Ubuntu---gedit 打开windows 下 .txt 文件乱码的解决方法

    问题出现情况:在windows 下编辑的 .txt 文件复制到 Ubuntu 下打开,默认打开方式为 gedit 软件打开,出现如下乱码: 出现原因:在 windows 系统下,.txt 文件默认编码 ...

  9. Cocos2d-x程序Windows下VC中文乱码的解决(用MultiByteToWideChar进行转换,VC2010有非常厉害的execution_character_set)

    Cocos2d-x默认字符串常量编码都是UTF8的,而Windows中的VC默认都是跟系统相同,比如简体Windows是GB2312或者GBK.繁体就是BIG5编码.而我们大多数中国人用VC编译出来的 ...

随机推荐

  1. Java6 WebService的发布(转)

      Java6 WebService的发布   转:http://lavasoft.blog.51cto.com/62575/227988/   WebService服务发布往往比较混乱,Axis2的 ...

  2. 转:一篇很全面的freemarker教程

    最近在使用freemarker,于是在网上找了一些教程学习,如下: 以下内容全部是网上收集: FreeMarker的模板文件并不比HTML页面复杂多少,FreeMarker模板文件主要由如下4个部分组 ...

  3. 如何用STAF进行自动化测试分布式运行

    本文的目的在于引导读者去了解STAF及如何调用其接口去实现自动化测试的分布式动行. 提到分布式运行,很多人想到了Jenkins,Jenkins里面有个node插件,可以去分派任务给slave,Jenk ...

  4. React Native工程修改Android包名

    默认初始化的React Native工程,生成Android工程的时候,包名默认是React Native工程的名字,跟一般Android工程com.company.xxx不一样. 这时候就需要手动修 ...

  5. MySQL表名不区分大小写的设置方法

    原来Linux下的MySQL默认是区分表名大小写的,通过如下设置,可以让MySQL不区分表名大小写:1.用root登录,修改 /etc/my.cnf:2.在[mysqld]节点下,加入一行: lowe ...

  6. 设计模式之策略模式&简单工厂模式

    学习设计模式已经有非常长一段时间了,事实上先前已经敲过一遍了.可是老认为没有学到什么,认识也不够深刻.如今趁着重构机房,再又一次来过,也不晚. 事实上在敲了机房之后,看看模式,事实上,曾经非常难理解. ...

  7. DataGridView控件使用大全说明-各种常用操作与高级操作

    DataGridView控件 DataGridView是用于Windows Froms 2.0的新网格控件.它可以取代先前版本中DataGrid控件,它易于使用并高度可定制,支持很多我们的用户需要的特 ...

  8. unity, particle system Emit from Edge

  9. xpath的常见操作

    1. 获取某一个节点下所有的文本数据: data = response.xpath('//div[@id="zoomcon"]') content = ''.join(data.x ...

  10. Windows平台JDK安装

    原文链接:http://android.eoe.cn/topic/android_sdk 下载Java的开发包JDK JDK有好几个类型版本,我们只需要选择Java SE类型的版本就行了.进入网页:h ...