记一次 .NET 某设备监控系统 死锁分析

news2024/11/15 13:44:59

一:背景

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锁,页锁,表锁,在必要的时候还会涉及到锁的升级,将性能,锁开销,一致性 做到了极致,非常值得我们研究和学习。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/806586.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【原理图专题】OrCAD Capture原理图设计差异对比

在我们进行硬件设计时,往往会遇到项目上的变更。比如更换了替代料、电路错误、新需求等等,这些有的需要动用到原理图,而动到原理图往往很容易出错,特别是已经量产的产品,我们可以通过BOM对比等方式来查看变更。那是否有一种其他方式能直接进行原理图对比呢? 其中对比功能…

桥接模式——处理多维度变化

1、简介 1.1、概述 桥接模式是一种很实用的结构型设计模式。如果软件系统中某个类存在两个独立变化的维度,通过该模式可以将这两个维度分离出来,使两者可以独立扩展,让系统更加符合单一职责原则。与多层继承方案不同,它将两个独…

【Linux】更换jdk版本

目录 一、前言二、查看jdk版本号1、项目中的版本号(pom.xml)2、服务器中的版本号 三、更换jdk版本1、创建java文件夹2、下载并解压JDK安装包①、下载jdk安装包②、移动到创建好的/usr/local/java路径下③、解压jdk安装包 四、删除原来的jdk版本1、删除原…

Java虚拟机——线程安全与锁优化

1 线程安全 当多个线程同时访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作。调用这个对象的行为都可以获得安全的结果,就称这个对象是线程安全…

从零开始理解Linux中断架构(23)中断运行临界区和占先调度

Linux在内核中定义了6种运行临界区。 in_interrupt in_interrupt在驱动中使用频率最高的函数了,in_interrupt()就是指示Core是否正在中断处理中,包含了硬中断,软中断运行临界区。如果在中断处理中,则不能调用__do_softirq执行软中断处理。硬中断中不可调度不可中断,所有…

智慧园区安保人员巡更巡检解决方案,蓝牙信标主动式蓝牙定位导航系统

一、需求分析 目前,大部分写字楼,工厂,学校,银行,车站等场景对安保人员的管理依然靠手填单子记录作业情况,在缺乏信息化手段的情况下,靠人员自觉性或者RFID巡更棒,在这些传统方式下…

2023 年牛客多校第四场题解

A Bobo String Construction 题意:给定一个 01 01 01 字符串 t t t,构造一个长度为 n n n 的 01 01 01 串 s s s,使得 t t t 在 c o n c a t ( t , s , t ) {\rm concat}(t, s, t) concat(t,s,t) 中仅出现两次。多测, 1 ≤…

Android杂录 语音转文字功能 python混合开发环境搭建 priv-app开机赋予麦克风权限 HDMI与麦克风同时工作配置

专栏目录: 高质量文章导航-持续更新中_GZVIMMY的博客-CSDN博客 一.语音转文字功能 软件架构 硬件架构: 耳机接口 报错类型: AudioFlinger could not create record track, status: -1 Androi

【雕爷学编程】Arduino动手做(175)---机智云ESP8266开发板模块7

37款传感器与执行器的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块,依照实践出真知(一定要动手做)的理念,以学习和交流为目的&am…

FreeRTOS之互斥量

什么是互斥量? 在多数情况下,互斥型信号量和二值型信号量非常相似,但是从功能上二值型信号量用于同步, 而互斥型信号量用于资源保护。 互斥型信号量和二值型信号量还有一个最大的区别,互斥型信号量可以有效解决优先级…

Python(四十八)列表的特点

❤️ 专栏简介:本专栏记录了我个人从零开始学习Python编程的过程。在这个专栏中,我将分享我在学习Python的过程中的学习笔记、学习路线以及各个知识点。 ☀️ 专栏适用人群 :本专栏适用于希望学习Python编程的初学者和有一定编程基础的人。无…

基于SpringBoot+Vue的藏区特产销售平台设计与实现(源码+LW+部署文档等)

博主介绍: 大家好,我是一名在Java圈混迹十余年的程序员,精通Java编程语言,同时也熟练掌握微信小程序、Python和Android等技术,能够为大家提供全方位的技术支持和交流。 我擅长在JavaWeb、SSH、SSM、SpringBoot等框架…

python调用百度ai将图片/pdf识别为表格excel

python调用百度ai将图片识别为表格excel 表格文字识别(异步接口)图片转excel 表格文字识别V2图片/pdf转excel通用 表格文字识别(异步接口) 图片转excel 百度ai官方文档:https://ai.baidu.com/ai-doc/OCR/Ik3h7y238 使用的是表格文字识别(异步接口),同步…

安装taghighlight遇到的需要python问题

起因: 在vim里面使用taghighlight时,输入命令:UpdateTypesFile后出现下面的提示: 看了一下,系统里面有安装python3.10,为什么还提示要安装python2.6呢?开始以为是python3.10的版本与taghighlight不匹配&am…

Linux常用命令——dpkg-split命令

在线Linux命令查询工具 dpkg-split Debian Linux中将大软件包分割成小包 补充说明 dpkg-split命令用来将Debian Linux中的大软件包分割成小软件包,它还能够将已分割的文件进行合并。 语法 dpkg-split(选项)(参数)选项 -S:设置分割后的每个小文件最…

LeetCode[1302]层数最深叶子节点的和

难度:Medium 题目: 给你一棵二叉树的根节点 root ,请你返回 层数最深的叶子节点的和 。 示例 1: 输入:root [1,2,3,4,5,null,6,7,null,null,null,null,8] 输出:15示例 2: 输入:r…

HTTP——二、简单的HTTP协议

本章将针对 HTTP 协议结构进行讲解,主要使用HTTP/1.1版本。学完这章,想必大家就能理解 HTTP 协议的基础了。 HTTP 一、HTTP协议用于客户端和服务器之间的通信二、通过请求和响应的交换达成通信三、HTTP是不保存状态的协议四、请求URI定位资源五、告知服…

GAMES101 笔记 Lecture12 Geometry3

目录 Mesh Operations: Geometry ProcessingMesh Subdivision (曲面细分)Mesh Simplification(曲面简化)Mesh Regularization(曲面正则化) Subdivision(细分)Loop Subdivision(Loop细分)如何来调整顶点位置呢?Loop Subdivision Result (Loop细分的结果) Catmull-Cla…

chatglm-6b量化推理指标记录

chatglm量化推理指标对比,单卡显存32G, 保持batchsize为64不变。通过不同的量化可以节省显存进而提升提升batch size,加快全量数据的推理速度。当然通过量化可以降低大模型的显存使用门槛。

Ae 效果:CC Toner

颜色校正/CC Toner Color Correction/CC Toner CC Toner(CC 色调)效果常用于对图像进行色调处理,可以实现双色调、三色调、五色调以及纯色的效果。 在某种程度上,与 Ae 自带的填充 Fill、色调 Tint以及三色调 Tritone等效果有相似…