Windows下MFC程序利用LockCop解决死锁
死锁现象:在训练的时候,点击“终止”按钮时不时会发生死锁。
检测工具: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解决死锁的更多相关文章
- Windows下mysql忘记密码的解决方法
Windows下mysql忘记密码的解决方法 mysql5.0 http://www.jb51.net/article/21984.htm方法一: 1.在DOS窗口下输入 net stop mysql ...
- windows下git bash中文乱码解决办法
一.解决办法1:(直接上图) 1.在git bash下,右键 出现下图,选择options: 2.选择“Text” 3.将“Character set”设置为 UTF-8 转:windows下git ...
- Windows下的程序及热键监视神器——Spy++
Windows下的程序及热键监视神器--Spy++ 背景 在使用Windows的时候,偶尔会发现某些应用程序的热键不生效了:又或是桌面弹出了弹框却并不知道这个弹框来自何处.例如,本人最近使用Vim的时 ...
- windows下的句柄利用
什么是句柄 维基百科:在程序设计中,句柄(handle)是Windows操作系统用来标识被应用程序所建立或使用的对象的整数.其本质相当于带有引用计数的智能指针.当一个应用程序要引用其他系统(如数据库. ...
- linux下QT程序输出乱码解决方法
参考文章:http://blog.csdn.net/jiang1013nan/article/details/6667871 http://my.oschina.net/zjlaobusi/blog/ ...
- Windows下 Mysql启动报1067解决方法
前几天刚入职安装了一下Mysql 刚开始能打开 今天去公司发现启动不了服务 报1067错误, 在网上查看了一些方法,好多种版本..以下是本人的解决方法 1.打开运行-事件查看器--Windows日 ...
- windows下控制台程序实现窗口显示
windows下实现窗口显示,如果限定是C/C++语言,并且是原生Windows支持,需要使用GDI或GDI+.一般是在Visual Studio里新建Win32应用程序,而不是Win32 conso ...
- Ubuntu---gedit 打开windows 下 .txt 文件乱码的解决方法
问题出现情况:在windows 下编辑的 .txt 文件复制到 Ubuntu 下打开,默认打开方式为 gedit 软件打开,出现如下乱码: 出现原因:在 windows 系统下,.txt 文件默认编码 ...
- Cocos2d-x程序Windows下VC中文乱码的解决(用MultiByteToWideChar进行转换,VC2010有非常厉害的execution_character_set)
Cocos2d-x默认字符串常量编码都是UTF8的,而Windows中的VC默认都是跟系统相同,比如简体Windows是GB2312或者GBK.繁体就是BIG5编码.而我们大多数中国人用VC编译出来的 ...
随机推荐
- linux 系统获得当前文件夹下存在的所有文件 scandir函数和struct dirent **namelist结构体[转]
linux 系统获得当前文件夹下存在的所有文件 scandir函数和struct dirent **namelist结构体 1.引用头文件#include<dirent.h> struct ...
- 程序员,不要让自己做兔子(updated) 网上最近流传的一个笑话,关于兔子,狼还有一只老虎的,故事 我就是想打你了,还需要什么理由吗?谁让你是兔子 项目经理是这样当的
程序员,不要让自己做兔子(updated) 前段时间和一个朋友聊天,酒席间向我抱怨他那段时间的郁闷:项目经理从客户那里拿来一个需求,实际上就是一个ppt描述,我这个朋友拿过来看后刚开始不觉得什么,一个 ...
- 进阶之路(基础篇) - 022 Arduino Leonardo 中文介绍(摘抄)
本文摘抄:http://www.arduino.cn/thread-1205-1-1.html 概述Arduino Leonardo是基于ATmega32u4一个微控制器板.它有20个数字输入/输出引 ...
- 【Oracle】Oracle索引
在关系数据库中,索引是一种与表有关的数据库结构,它可以使对应于表的SQL语句执行得更快.索引的作用相当于图书的目录,可以根据目录中的页码快速找到所需的内容. 对于数据库来说,索引是一个必选项,但对于现 ...
- Swift 类型别名
类型别名 在 Swift 语言中使用 typealias 关键字定义类型别名. typealias ShortInteger = Int8
- VM页面中遍历枚举类
1)自定义的枚举类如下所示: public enum BusType { MID_SMALL(1, "中小件"), FRESH(2, "生鲜"), GLOBAL ...
- mysql 1449 : The user specified as a definer ('root'@'%') does not exist
1)创建试图时抛出此错误信息,如下图所示: 2)从网上搜索了一下,是SQL权限问题,通过如下的方式便可以解决: 3)再次执行创建视图的语句,验证一下问题是否已经解决,可以了,如下所示: 4)参考如下所 ...
- 日志收集之--将Kafka数据导入elasticsearch
最近需要搭建一套日志监控平台,结合系统本身的特性总结一句话也就是:需要将Kafka中的数据导入到elasticsearch中.那么如何将Kafka中的数据导入到elasticsearch中去呢,总结起 ...
- MySQL Cluster
MySQL Cluster MySQL集群一个非共享(shared nothing).分布式.分区系统,使用同步复制机制提供高可用和高性能. MySQL集群使用的是NDB引擎.NDB存储引擎会在节点间 ...
- 一个“蝇量级” C 语言协程库
协程(coroutine)顾名思义就是“协作的例程”(co-operative routines).跟具有操作系统概念的线程不一样,协程是在用户空间利用程序语言的语法语义就能实现逻辑上类似多任务的编程 ...