記一次 .NET 某設備監控系統 死鎖分析

来源:https://www.cnblogs.com/huangxincheng/archive/2023/04/06/17292169.html
-Advertisement-
Play Games

一:背景 1. 講故事 上周看了一位訓練營朋友的dump,據朋友說他的程式卡死了,看完之後發現是一例經典的死鎖問題,蠻有意思,這個案例算是學習 .NET高級調試 入門級的案例,這裡和大家分享一下。 二:WinDbg 分析 1. 程式為什麼會卡死 因為是窗體程式,所以看主線程的線程棧就好了,如果卡在 ...


一:背景

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鎖,頁鎖,表鎖,在必要的時候還會涉及到鎖的升級,將性能,鎖開銷,一致性 做到了極致,非常值得我們研究和學習。

圖片名稱
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 前言 一、人物簡介 第一位閃亮登場,有請今後會一直教我們C語言的老師 —— 自在。 第二位上場的是和我們一起學習的小白程式猿 —— 逍遙。 二、構成和表示方式 邏輯運算符是用來比較和操作布爾值的運算符 C語言中的邏輯運算符主要有3個,如下表所示 | 運算符 | 名稱 | 示例 | 描述 | | : ...
  • 教程簡介 什麼是SEO和SEO文案 - 一個關於搜索引擎優化(SEO)入門教程,以瞭解什麼是SEO和各種SEO工具和技術,包括白帽黑帽Spamdexing和Meta標簽關鍵詞主題標題超鏈接圖像網頁優化和搜索引擎抓取索引處理相關性計算結果檢索隱藏元標記填充門口網關頁面劫持 搜索引擎優化,又稱為SEO, ...
  • 1.簡介 在實際項目中,我們可能需要在執行代碼的過程中動態導入包並執行包中的相應內容,通常情況下,我們可能會將所需導入的包及對象以字元串的形式傳入,例如test.test.run,下麵將介紹如何動態導入。 假設存在如下包: 其中test.py的內容如下: count = 1 def run(): p ...
  • 教程簡介 社交媒體營銷入門教程 - IT寶庫整理的從簡單的步驟瞭解社交媒體營銷,從基本到高級概念,包括簡介,Facebook營銷,Twitter,Linkedin,Google+,pinterest,視頻營銷,社會書簽,圖像優化,博客,社交媒體分析,監控社交媒體賬戶。 教程目錄 社交媒體營銷教程 S ...
  • 一、前言 在2.0的架構設計中,引入了插件的概念,目的就是為瞭解決代碼臃腫問題,隨著系統功能越來越多,System層的代碼也越來越多,之前是以文件夾的方式區分功能模塊,這樣的話代碼就過於集中,想找到某一個功能模塊的代碼就要翻好幾個文件夾,不利於以後的開發和維護。所以在新的架構中,我將部分功能模塊通過 ...
  • 在WPF中TextBlock的Text有時內容只需要改變個別數字,而不需要所以內容都修改,這時候就要使用StringFormat, 如: <TextBlock Text="I have xxx friends"/> 這裡面的xxx是個變數,那在Binding時應該怎樣寫呢 <TextBlock Te ...
  • 項目結構如下圖所示: admin和demo是兩個api服務。gateway是一個ocelot網關服務。 第一步在ocelot.json添加如下配置: 第二步:在ocelot項目修改以下幾處: demo api和admin api不需要修改什麼,只要以前正常打開,現在就可以在網關服務的swagger上 ...
  • 1、避免在迴圈中進行查詢操作: 避免在迴圈中進行查詢操作,可以將查詢結果緩存到記憶體中,然後對記憶體中的數據進行操作,可以提高性能。這種方式適合集合數據量少的數據,否則利大於弊。 // 不建議的方式:在迴圈中進行查詢操作 foreach (var item in itemList) { var resu ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...