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

检测工具: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. Linux下mysql的远程连接(转)

    转载:http://www.cnblogs.com/fnlingnzb-learner/p/5830661.html 如果Mysql是按上篇的方法进行安装和设置的话,那进行远程连接就会稍微简单一点.我 ...

  2. 蓝牙进阶之路 (003) - AT指令(转)

    一 . 一 般 命 令 1.AT+CGMI      给出模块厂商的标识. 2.AT+CGMM    获得模块标识.这个命令用来得到支持的频带(GSM 900,DCS 1800    或PCS 190 ...

  3. 如果没有 Android 世界会是什么样子?

    2005年谷歌从安迪·鲁宾(Andy Rubin)手中收购Android系统,起初安迪·鲁宾(Andy Rubin)只是想为数码相机开发出一个更为先进的系统,所以有了 Android.但是智能手机行业 ...

  4. 《JAVA与模式》之解释器模式 (转载)

    一.引子 其实没有什么好的例子引入解释器模式,因为它描述了如何构成一个简单的语言解释器,主要应用在使用面向对象语言开发编译器中:在实际应用中,我们可能很少碰到去构造一个语言的文法的情况. 虽然你几乎用 ...

  5. golang学习笔记---函数、方法和接口

    函数:对应操作序列,是程序的基本组成元素. 函数有具名和匿名之分:具名函数一般对应于包级的函数,是匿名函数的一种特例,当匿名函数引用了外部作用域中的变量时就成了闭包函数,闭包函数是函数式编程语言的核心 ...

  6. ios app: 使用企业license设置发布app的过程

      ios开发者证书与企业证书的内容,关系,以及ios app 使用企业license设置发布app的过程 iOS是一个非常封闭的系统.授权文件(.mobileprovision)和签名证书文件(.c ...

  7. 【转】java io 流 设计模式

    知识点:什么是装饰模式: http://wenku.baidu.com/view/ad4eac9f51e79b896802263b.html(原理讲的很清楚) http://wenku.baidu.c ...

  8. Android Studio 环境搭建参考,jdk10javac命令提示不是内部或外部命令

    https://blog.csdn.net/qq_33658730/article/details/78547789 win10下Android Studio和SDK下载.安装和环境变量配置 http ...

  9. SQLServer2005 CASE WHEN在项目中实例-查询显示值替换

    1.利用SqlServer中的case when来把数据查询出来的数据替换成其它值显示 2.结果对比: 普通select查询出来的结果如下: 用了case when方法后显示结果如下: 3.具体使用代 ...

  10. ROC 曲线简要解释

    阳性 (P, positive)阴性 (N, Negative)真阳性 (TP, true positive):正确的肯定.又称:命中 (hit)真阴性 (TN, true negative):正确的 ...