1、现象
   最近项目中调出一个bug,某些时候程序会卡死不动,用windbg进行加载后用 ~*kb 命令列出所有的线程栈调用,发现有多个线程调用 WaitForMultipleObjects 在等待同一个内核对象:

   输入 !handle cc f 命令列出该内核对象的详细信息:

   发现是是一个Mutex对象,对象名是 Mutex_DebugMsg2 ,查找代码知道这个Mutex是用写log时锁定文件写入用的。代码如下:

BOOL	CDebugMsg::WriteLogA(LPSTR pszLog)
{
#ifdef _WRITEDEBUGMSGLOG
BOOL bRet = FALSE; if (WaitForSingleObject(m_hMutex, INFINITE) != WAIT_OBJECT_0)
{
return bRet;
} FILE* fp = _tfopen(m_ptszLogPath, _T("a+"));
if (fp)
{
TCHAR ptszTime[16] = {0};
char pszWriteMsg[MAXDEBUGMSGCHARNUM] = {0}; GetTimeString(ptszTime); USES_CONVERSION;
_snprintf(pszWriteMsg, MAXDEBUGMSGCHARNUM, "%s %s\n", T2A(ptszTime), pszLog);
fputs(pszWriteMsg, fp);
fclose(fp);
bRet = TRUE;
} ReleaseMutex(m_hMutex);
return bRet; #endif return FALSE;
}

2、Windbg查找Mutex所有者
   为了知道那个线程占用了Mutex没有释放,参考此篇文章说明(http://blog.csdn.net/gufeng99/article/details/46714711)
   同时,为了简化问题原因查找,写了小Demo起3个线程调用log记录代码,触发上述bug,后开启windbg进入内核调试模式。
2.1 查看测试Demo进程

2.2 查看进程的所有线程及等待状态信息


   发现有3个线程都在等待同一个Mutex,线程所有者是0xfffffa8004d1b590

2.3 查看Mutex所有者线程信息,看是那个线程在占用


   找到该线程所在进程的PID = 0xC30,也就是本进程,线程ID = 0xD74

2.4 停止内核附加,切换Windbg改用用户态调试附加Demo进程,列出所有线程信息及调用栈


       发现线程拥有者就是主线程,难道是主线程调用后未释放成功么?为了验证这个情况,于是在ReleaseMutex(m_hMutex)加上返回跟踪输出,结果每次释放都是成功的。再看看代码,可能的解释不多了,想得到的可能就是WaitForSingleObject返回了,但返回值不是WAIT_OBJECT_0,导致没有调用ReleaseMutex进行释放。于是再跟踪了下WaitForSingleObject的返回值,发现第一次调用时返回值是128。
      去翻了下《windows核心编程》一书,原来Mutex和其他内核对象不一样,当拥有Mutex的线程未进行释放时被终止,这时Mutex处于被废弃状态,其他线程WaitForSingleObject可立即获得Mutex的所有权,但返回值会是WAIT_ABANDONED(128),而并不是WAIT_OBJECT_0,这是Mutex非常特殊的一点。
       到了这里,原因就非常明了了,由于log输出在项目中使用非常频繁,而写log时又需要重复调用fopen()和fclose打开关闭文件,这对IO的操作是比较费时间的,导致某些线程在此处由于等待超时被终止(由于其他Mutex是可跨进程的,所以其他进程中的线程意外终止或进程未正常退出都会有此问题),进而导致Mutex未被释放处于废弃状态。而程序收到WAIT_ABANDONED后未是Mutex进行释放,导致Mutex死锁。这里再次深刻的警示,线程是不能随意终止的,同时频繁打开关闭文件操作也是不合理的,正确的做法应该是打开一次后保存文件句柄,再程序退出时再关闭。
      另由于这个bug不是必现(由于线程不是每次都会由于等待超时被终止),在调试过程中我保存了下dump,后来调试下dump的时候显示handle信息是发现一个非常有意思的事情:

      dump中在调用 !handle cc f 命令后,同时也将Mutex的拥有者线程也显示出来了,而在即时调试的时候却没有该信息,需要绕一圈到内核中去查找。

一次Mutex死锁的原因探究的更多相关文章

  1. 查询在应用程序运行得很慢, 但在SSMS运行得很快的原因探究

    原文:查询在应用程序运行得很慢, 但在SSMS运行得很快的原因探究 查询在应用程序运行得很慢, 但在SSMS运行得很快的原因探究 -理解性能疑点 1      引言 内容来自http://www.so ...

  2. java中多线程产生死锁的原因以及解决意见

    1.  java中导致死锁的原因 多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放,而该资源又被其他线程锁定,从而导致每一个线程都得等其它线程释放其锁定的资源,造成了所有线程都无法正常结 ...

  3. SQL Server死锁产生原因及解决办法 .

    其实所有的死锁最深层的原因就是一个:资源竞争 表现一: 一个用户A 访问表A(锁住了表A),然后又访问表B,另一个用户B 访问表B(锁住了表B),然后企图访问表A,这时用户A由于用户B已经锁住表B,它 ...

  4. java 死锁产生原因及解锁(转)

    原文地址 进程死锁及解决办法 一.要点提示 (1) 掌握死锁的概念和产生死锁的根本原因. (2) 理解产生死锁的必要条件--以下四个条件同时具备:互斥条件.不可抢占条件.占有且申请条件.循环等待条件. ...

  5. 使用JDK自带的工具jstack找出造成运行程序死锁的原因

    Java多线程编程也是Java面试中经常考察的内容.刚接触Java多线程编程的朋友们,可能会不慎写出一些会导致死锁(deadlock)的应用出来.如何分析造成Java多线程的原因呢?很多时候我们在怀疑 ...

  6. 谁在死锁Mutex——用Windbg查找Mutex死锁所有者线程

    Who is blocking that Mutex? - Fun with WinDbg, CDB and KD 05 Aug 2006 By Ingo Rammer I'm currently t ...

  7. MSSQL死锁产生原因及解决方法

    一.    什么是死锁 死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去.此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等的进 ...

  8. nignx+php-fpm环境下 phpmyadmin打开空白的原因探究

    打开phpmyadmin一直是空白的,发现是js的问题,原因是pma的js/get_script_js.php读取js不完整 很容易的将问题原因想到了php的输出缓存大小上,我把php.ini里的ou ...

  9. Windbg调试互斥体(Mutex)死锁

    一. 测试代码 #include <windows.h> #include <tchar.h> #include <process.h> HANDLE hMutex ...

随机推荐

  1. SQLServer2008部署镜像

    SQL Server 2008 R2数据库镜像部署 概述 “数据库镜像”是一种针对数据库高可用性的基于软件的解决方案.其维护着一个数据库的两个相同的副本,这两个副本分别放置在不同的SQL Server ...

  2. linux常用命令之文件权限

    CHGRP chgrp – change group ownership,改变组别的所有权 用法 chgrp 组别名 文件\目录名 选项 -R  (recursive)递归改变,将目录下所有的文件和目 ...

  3. 20款免费响应式的 HTML5 网站模板下载

    今天这篇文章给大家带来的是20款免费响应式的 HTML5 网站模板,大家可以借助这些优秀的网站模板创建自己的优秀网站.响应式(Responsive Design)设计的目标是要让产品界面能够响应用户的 ...

  4. MySQL的常规操作

    MySQL的常规知识 show 查看数据库 show databases; 查看表 存在的所有表 show tables; 创建表的命令 show create table table_name; 表 ...

  5. 使用ruby搭建简易的http服务和sass环境

    使用ruby搭建简易的http服务和sass环境 由于在通常的前端开发情况下,我们会有可能需要一个http服务,当然你可以选择自己写一个node的http服务,也比较简单,比如下面的node代码: v ...

  6. 【初探移动前端开发03】jQuery Mobile(上)

    前言 到目前为止,我打了几天酱油了,这几天落实了工作,并且看了一部电视连续剧(陈道明-手机),我很少看连续剧了,但是手机质量很高啊,各位可以看看. 我们今天先学习一下jquery mobile的基础知 ...

  7. MySQL索引类型 btree索引和hash索引的区别

    来源一 Hash 索引结构的特殊性,其检索效率非常高,索引的检索可以一次定位,不像B-Tree 索引需要从根节点到枝节点,最后才能访问到页节点这样多次的IO访问,所以 Hash 索引的查询效率要远高于 ...

  8. ASP.NET程序中常用的三十三种代码

    1. 打开新的窗口并传送参数: 传送参数: response.write("<script>window.open(’*.aspx?id="+this.DropDown ...

  9. JavaScript学习笔记-对象

    枚举对象的属性:通常用for(...in...)来循环遍历,由于 for in 总是要遍历整个原型链,因此如果一个对象的继承层次太深的话会影响性能 for(var i in foo){ if(foo. ...

  10. spring web MVC

    详情:http://blog.csdn.net/mic_hero/article/details/50237627