记一次 .NET 某设备监控系统 死锁分析
一:背景
1. 讲故事
上周看了一位训练营朋友的dump,据朋友说他的程序卡死了,看完之后发现是一例经典的死锁问题,蛮有意思,这个案例算是学习 .NET高级调试
入门级的案例,这里和大家分享一下。
二:WinDbg 分析
1. 程序为什么会卡死
因为是窗体程序,所以看主线程的线程栈就好了,如果卡在 用户态
那这个问题相对容易解决,如果卡在 内核态
这个问题就比较复杂了,需要开启 WinDbg 的本机内核调试或者双机调试才能找到最终的问题。
既然已经说了是入门级,那肯定是卡在 用户态
层面啦,我们用 !clrstack
命令观察下主线程的线程栈即可,输出如下:
0:000:x86> !clrstack
OS Thread Id: 0x31d8 (0)
Child SP IP Call Site
00f9ec28 00e9e108 [GCFrame: 00f9ec28]
00f9ed08 00e9e108 [GCFrame: 00f9ed08]
00f9ed24 00e9e108 [HelperMethodFrame_1OBJ: 00f9ed24] System.Threading.Monitor.ReliableEnter(System.Object, Boolean ByRef)
00f9eda0 70c08468 System.Threading.Monitor.Enter(System.Object, Boolean ByRef) [f:\dd\ndp\clr\src\BCL\system\threading\monitor.cs @ 62]
00f9edb0 0ce916c7 xxxx.GetAlarmCount(xxx)
00f9ee28 0961f41f xxx.xxx()
00f9ef04 0961d60a xxxx.xxx(System.Object, System.EventArgs)
00f9ef50 6de03dc9 System.Windows.Forms.Timer.OnTick(System.EventArgs)
00f9ef58 6de053d9 System.Windows.Forms.Timer+TimerNativeWindow.WndProc(System.Windows.Forms.Message ByRef)
00f9ef64 6ddd38d0 System.Windows.Forms.NativeWindow.Callback(IntPtr, Int32, IntPtr, IntPtr)
00f9f1b0 0130d5d4 [InlinedCallFrame: 00f9f1b0]
00f9f1ac 6de375bd DomainBoundILStubClass.IL_STUB_PInvoke(MSG ByRef)
00f9f1b0 6dde44e3 [InlinedCallFrame: 00f9f1b0] System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG ByRef)
00f9f1e4 6dde44e3 System.Windows.Forms.Application+ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr, Int32, Int32)
00f9f1e8 6dde40d1 [InlinedCallFrame: 00f9f1e8]
00f9f270 6dde40d1 System.Windows.Forms.Application+ThreadContext.RunMessageLoopInner(Int32, System.Windows.Forms.ApplicationContext)
00f9f2c0 6dde3f23 System.Windows.Forms.Application+ThreadContext.RunMessageLoop(Int32, System.Windows.Forms.ApplicationContext)
00f9f2ec 6ddbc83d System.Windows.Forms.Application.Run(System.Windows.Forms.Form)
00f9f300 01350a6e CleanControl.Program.Main(System.String[])
00f9f4ec 71d00556 [GCFrame: 00f9f4ec]
...
从卦中看,主线程卡在 Monitor.Enter
处,也就表明当前线程在 GetAlarmCount()
方法的一个 lock 处等待。
2. 谁在持有锁
要想找到谁在持有锁,需要理解 lock
的底层机制,它是建立在 AutoResetEvent + ObjectHeader
基础之上的一种锁玩法,在 CLR 层面使用 SyncBlk
的 class 来承载的,参考如下代码:
class SyncBlock
{
// ObjHeader creates our Mutex and Event
friend class ObjHeader;
friend class SyncBlockCache;
friend struct ThreadQueue;
#ifdef DACCESS_COMPILE
friend class ClrDataAccess;
#endif
friend class CheckAsmOffsets;
protected:
AwareLock m_Monitor; // the actual monitor
SLink m_Link;
DWORD m_dwHashCode;
WCHAR m_BSTRTrailByte;
}
要想观察这些 SyncBlk
信息,可以用 WinDbg 提供的快捷命令 !syncblk
来观察。
0:000:x86> !syncblk
Index SyncBlock MonitorHeld Recursion Owning Thread Info SyncBlock Owner
180 0b86e8e8 3 1 01452a08 3728 24 039da140 System.Object
-----------------------------
Total 339
CCW 5
RCW 2
ComClassFactory 0
Free 4
从卦中看,当前持有 lock 的线程是 24
号,那这个线程为什么迟迟不退出锁呢? 这就需要到这个线程栈上找原因了, 使用命令 ~24s; !clrstack
即可。
0:004:x86> ~24s
ntdll_779a0000!NtWaitForMultipleObjects+0xc:
77a11b2c c21400 ret 14h
0:024:x86> !clrstack
OS Thread Id: 0x3728 (24)
Child SP IP Call Site
0e99e504 0000002b [HelperMethodFrame_1OBJ: 0e99e504] System.Threading.WaitHandle.WaitOneNative(System.Runtime.InteropServices.SafeHandle, UInt32, Boolean, Boolean)
0e99e5e8 70bdd952 System.Threading.WaitHandle.InternalWaitOne(System.Runtime.InteropServices.SafeHandle, Int64, Boolean, Boolean) [f:\dd\ndp\clr\src\BCL\system\threading\waithandle.cs @ 243]
0e99e600 70bdd919 System.Threading.WaitHandle.WaitOne(Int32, Boolean) [f:\dd\ndp\clr\src\BCL\system\threading\waithandle.cs @ 194]
0e99e614 6e4aa4a8 System.Windows.Forms.Control.WaitForWaitHandle(System.Threading.WaitHandle)
0e99e654 6e8585af System.Windows.Forms.Control.MarshaledInvoke(System.Windows.Forms.Control, System.Delegate, System.Object[], Boolean)
0e99e658 6e4acc4f [InlinedCallFrame: 0e99e658]
0e99e6e0 6e4acc4f System.Windows.Forms.Control.Invoke(System.Delegate, System.Object[])
...
0e99e83c 0f46512c xxx.AddAlarmQueue(xxx)
...
0e99ea84 0d3f2783 xxx.Func()
0e99ead8 70be2e01 System.Threading.ThreadHelper.ThreadStart_Context(System.Object) [f:\dd\ndp\clr\src\BCL\system\threading\thread.cs @ 74]
...
从卦中看,其中的 MarshaledInvoke
方法很刺眼,它表示工作线程通过 Invoke 向主线程的控件推送数据,因为主线程迟迟没有响应它,导致它一直在等待,而恰恰它又持有了 lock 锁,不赶巧主线程因为获取lock在迟迟等待又无法响应工作线程的 MarshaledInvoke 请求,导致一种死锁状态,如果要画个图大概是这样的。
3. 如何化解
寻得化解之法,需要看下程序中是怎么持有 lock 锁的,仔细观察代码之后,终于找到了 lock 代码处,截图如下:
对代码敏感得朋友相信一眼就能看出,这 lock 的粒度真tmd的大,只要 lock 中有一处调用了 Invoke,如果不凑巧主线程刚好在等待 lock ,那就死锁了,正如本篇中的 死锁。
三:总结
这次卡死事故,本质上来说是程序员对锁
的使用没有一个好的习惯,没有遵循锁的尽早释放原则。
其实这一块关系型数据库做的特别好,锁的粒度分的很细,诸如:行锁,RID锁,Key锁,页锁,表锁,在必要的时候还会涉及到锁的升级,将性能,锁开销,一致性 做到了极致,非常值得我们研究和学习。
记一次 .NET 某设备监控系统 死锁分析的更多相关文章
- xx系统属性分析
在本周的课程学习当中,我们简单了解到系统的一些属性,同时在课下也对<大型网站技术架构:核心原理与案例分析>进行了初步的阅读. 在书籍中我看到了许多其他的知识,也对课堂学习的知识有了巩固,现 ...
- 一次 MySQL 线上死锁分析实战
关键词:MySQL Index Merge 前言 MySQL 的锁机制相信大家在学习 MySQL 的时候都有简单的了解过,那既然有锁就必定绕不开死锁这个问题.其实 MySQL 在大部分场景下是不会存在 ...
- 构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(34)-文章发布系统①-简要分析
原文:构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(34)-文章发布系统①-简要分析 系列目录 最新比较闲,为了学习下Android的开发构建ASP.NET ...
- Tomcat系统架构分析
Tomcat系统架构分析 关于这边blog呢,实际开发中并不会用到,但是我觉得还是很有必要认真的写一下.毕竟我们每天在本地撸码的时候使用的就是tomcat来做web服务器.一个常识就是说我们本地在to ...
- Linux系统IO分析工具之iotstat常用参数介绍
Linux系统IO分析工具之iotstat常用参数介绍 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 1>.安装iostat [root@flume115 ~]# yum - ...
- Linux系统IO分析工具之iotop常用参数介绍
Linux系统IO分析工具之iotop常用参数介绍 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 在一般运维工作中经常会遇到这么一个场景,服务器的IO负载很高(iostat中的 ...
- Android基础-系统架构分析,环境搭建,下载Android Studio,AndroidDevTools,Git使用教程,Github入门,界面设计介绍
系统架构分析 Android体系结构 安卓结构有四大层,五个部分,Android分四层为: 应用层(Applications),应用框架层(Application Framework),系统运行层(L ...
- Windows系统内存分析工具的介绍
Windows系统内存分析工具的介绍(进程管理器,资源管理器,性能监视器, VMMap, RamMap,PoolMon) 微软官方提供多种工具来分析Windows 的内存使用情况,除了系统自带的任 ...
- Java互联网架构-Mysql分库分表订单生成系统实战分析
概述 分库分表的必要性 首先我们来了解一下为什么要做分库分表.在我们的业务(web应用)中,关系型数据库本身比较容易成为系统性能瓶颈,单机存储容量.连接数.处理能力等都很有限,数据库本身的“有状态性” ...
- linux系统瓶颈分析(精)
linux系统瓶颈分析(精) (2013-09-17 14:22:00) 分类: linux服务器瓶颈分析 1.0 性能监控介绍 性能优化就是找到系统处理中的瓶颈以及去除这些的过程,多数管理员相信 ...
随机推荐
- Flutter Web预览时白屏解决方法
原因是因为运行 flutter run 是自动选择渲染器 桌面端WEB浏览器默认使用 CanvasKit渲染器 移动端WEB浏览器默认使用 HTML渲染器 问题就出在了CanvasKit渲染器,他 ...
- nuxt.js的导航守卫
一.使用router.js 重构项目时还行使用原来的router.js也是可以的,需要下载插件惊醒配置,这时候vue-cli中怎么用,nuxt中就怎么使用导航守卫,几乎一样 二.使用nuxt.js 1 ...
- 4组-Beta冲刺-3/5
一.基本情况 队名:摸鲨鱼小队 组长博客:https://www.cnblogs.com/smallgrape/p/15600609.html github链接:https://github.com/ ...
- tail 命令 没有内容输出,记录一种解决
1. 没有内容输出的命令: tail - f /test/platform/jtimer.log 控制台: 2. 当切换到此文件的当前路径后才有: 使用: tail - f jtimer.log
- pip 临时切换 国内源
sudo pip install -i https://pypi.tuna.tsinghua.edu.cn/simple some-package
- FCC 高级算法题 收银机找零钱
Exact Change 设计一个收银程序 checkCashRegister() ,其把购买价格(price)作为第一个参数 , 付款金额 (cash)作为第二个参数, 和收银机中零钱 (cid) ...
- 利用shell脚本提高访问GitHub速度
Github由于做了域名限制,所以访问比较慢,编写了个脚本达到做本地域名解析提高GitHub的访问速度 #!/usr/bin/env bash # 该脚本用来提升github的访问速度 ROOT_UI ...
- ROS突发限速参考资料
MT-Rate-Limit - Datarate limitation for clients. Format is: rx-rate[/tx-rate] [rx-burst-rate[/tx-bur ...
- leedcode 刷题-V2
leetcode 字符串类 数组类 链表类 树类 二叉树类 图类 数学类 数据结构类 1. 稀疏相似度 (倒排索引) (https://leetcode-cn.com/problems/sparse- ...
- density plot
FIN=read.table("/Users/zhongyuantian/macshare/workSpace2021/1.TFY/1.3.TFY20201215/1.3.1.TFY1C_T ...