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

检测工具: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. windbg cs

    !cs 扩展显示一个或多个临界区(critical section)或者整个临界区树 !cs Address 指定要显示的临界区地址.如果省略该参数,调试器显示当前进程中所有临界区.   :> ...

  2. windows下安装phpredis模块 (转)

    1.下载: http://pecl.php.net/package/redis/2.2.7/windows 2.下载后 由于里面有两个模块分别是vc6,vc9编译的,我们需要知道我们的Php是vc6还 ...

  3. 百度OCR识别示例

    文章地址:https://www.cnblogs.com/Charltsing/p/OcrAnswerer.html 最新版为v4.1版,开放一定概率的八窗口体验功能,请截图体验(多点几次图片).更新 ...

  4. 【Linux】双向重导向命令tee

    想个简单的东西,我们知道 > 会将数据流整个传送给文件或装置,因此我们除非去读取该文件或装置, 否则就无法继续利用这个数据流.万一我想要将这个数据流的处理过程中将某段信息存下来,应该怎么做? 利 ...

  5. jquery实现点击展开列表同时隐藏其他列表 js 对象操作 对象原型操作 把一个对象A赋值给另一个对象B 并且对象B 修改 不会影响 A对象

    这篇文章主要介绍了jquery实现点击展开列表同时隐藏其他列表的方法,涉及jquery鼠标事件及节点的遍历与属性操作技巧,具有一定参考借鉴价值,需要的朋友可以参考下 本文实例讲述了jquery实现点击 ...

  6. Oracle 12C -- 不同容器之间切换

    1.使用connect SQL> show con_name; CON_NAME ------------------------------ CDB$ROOT SQL> connect ...

  7. Delphi GDI对象之剪切区域

    原文链接: http://www.cnblogs.com/pchmonster/archive/2012/07/05/2577627.html 剪切区域(Clipping Regions) Regio ...

  8. Groovy 学习手册(1)

    1. 需要安装的软件 Java / Groovy 对应 Java 和 Groovy,你需要安装以下软件: Java JDK,例如 JDK 8 IDE,例如 Eclipse,NetBeans 8 Gro ...

  9. Android工具类-关于网络、状态的工具类

    下方是一个很好的监测网络.状态的工具类 public class NetworkUtils { /** * 网络是否可用 * * @param activity * @return */ public ...

  10. PySpark 行列转换

    Spark实现行列转换pivot和unpivot 背景 做过数据清洗ETL工作的都知道,行列转换是一个常见的数据整理需求. 首先明确一下啥叫行列转换,因为这个叫法也不是很统一,有的地方叫转置,有的地方 ...