一:背景1.講故事這篇文章起源于昨天的一位朋友發(fā)給我的dump文件,說(shuō)它的程序出現(xiàn)了卡死,看了下程序的主線程棧,居然又碰到了 OnUserPreferenceChanged 導(dǎo)致的掛死問題,真的是經(jīng)典中的經(jīng)典,線程棧如下:
0:000:x86> !clrstackOS Thread Id: 0x4eb688 (0)Child SPIP Call Site002fed38 0000002b [HelperMethodFrame_1OBJ: 002fed38] System.Threading.WaitHandle.WaitOneNative(System.Runtime.InteropServices.SafeHandle, UInt32, Boolean, Boolean)002fee1c 5cddad21 System.Threading.WaitHandle.InternalWaitOne(System.Runtime.InteropServices.SafeHandle, Int64, Boolean, Boolean)002fee34 5cddace8 System.Threading.WaitHandle.WaitOne(Int32, Boolean)002fee48 538d876c System.Windows.Forms.Control.WaitForWaitHandle(System.Threading.WaitHandle)002fee88 53c5214a System.Windows.Forms.Control.MarshaledInvoke(System.Windows.Forms.Control, System.Delegate, System.Object[], Boolean)002fee8c 538dab4b [InlinedCallFrame: 002fee8c]002fef14 538dab4b System.Windows.Forms.Control.Invoke(System.Delegate, System.Object[])002fef48 53b03bc6 System.Windows.Forms.WindowsFormsSynchronizationContext.Send(System.Threading.SendOrPostCallback, System.Object)002fef60 5c774708 Microsoft.Win32.SystemEvents+SystemEventInvokeInfo.Invoke(Boolean, System.Object[])002fef94 5c6616ec Microsoft.Win32.SystemEvents.RaiseEvent(Boolean, System.Object, System.Object[])002fefe8 5c660cd4 Microsoft.Win32.SystemEvents.OnUserPreferenceChanged(Int32, IntPtr, IntPtr)002ff008 5c882c98 Microsoft.Win32.SystemEvents.WindowProc(IntPtr, Int32, IntPtr, IntPtr)...說(shuō)實(shí)話,這種dump從去年看到今年,應(yīng)該不下五次了,都看煩了,其形成原因是:
- 未在主線程中生成用戶控件,導(dǎo)致用 WindowsFormsSynchronizationContext.Send 跨線程封送時(shí),對(duì)方無(wú)法響應(yīng)請(qǐng)求進(jìn)而掛死
為了不讓這些朋友的遺憾延續(xù)下去,這一篇做一個(gè)系統(tǒng)歸納,希望能助這些朋友一臂之力 。
二:解決方案1. 背景這個(gè)問題的形成詳情,我在去年的一篇文章為:記一次 .NET 某新能源汽車鋰電池檢測(cè)程序 UI掛死分析
https://www.cnblogs.com/huangxincheng/p/15245554.html 中已經(jīng)做過分享,因?yàn)?dump 中找不到問題的 Control,所以也留下了一些遺憾,這一篇就做個(gè)補(bǔ)充 。2. 問題突破點(diǎn)分析熟悉 WinForm 底層的朋友應(yīng)該知道,一旦在 工作線程 上創(chuàng)建了 Control 控件,框架會(huì)自動(dòng)給這個(gè)線程配備一個(gè)
WindowsFormsSynchronizationContext 和其底層的 MarshalingControl ,這個(gè)是有源碼支撐的,大家可以找下 Control 的構(gòu)造函數(shù),簡(jiǎn)化后的源碼如下:public class Control : Component{internal Control(bool autoInstallSyncContext){//***if (autoInstallSyncContext){WindowsFormsSynchronizationContext.InstallIfNeeded();}}}public sealed class WindowsFormsSynchronizationContext : SynchronizationContext, IDisposable{private Control controlToSendTo;private WeakReference destinationThreadRef;public WindowsFormsSynchronizationContext(){DestinationThread = Thread.CurrentThread;Application.ThreadContext threadContext = Application.ThreadContext.FromCurrent();if (threadContext != null){controlToSendTo = threadContext.MarshalingControl;}}internal static void InstallIfNeeded(){try{SynchronizationContext synchronizationContext = AsyncOperationManager.SynchronizationContext;if (synchronizationContext == null || synchronizationContext.GetType() == typeof(SynchronizationContext)){AsyncOperationManager.SynchronizationContext = new WindowsFormsSynchronizationContext();}}finally{inSyncContextInstallation = false;}}}public sealed class WindowsFormsSynchronizationContext : SynchronizationContext, IDisposable{public WindowsFormsSynchronizationContext(){DestinationThread = Thread.CurrentThread;Application.ThreadContext threadContext = Application.ThreadContext.FromCurrent();if (threadContext != null){controlToSendTo = threadContext.MarshalingControl;}}}internal sealed class ThreadContext{internal Control MarshalingControl{get{lock (this){if (marshalingControl == null){marshalingControl = new MarshalingControl();}return marshalingControl;}}}}
經(jīng)驗(yàn)總結(jié)擴(kuò)展閱讀
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 打羽毛球一個(gè)小時(shí)相當(dāng)于跑步多久
- 【炫麗】從0開始做一個(gè)WPF+Blazor對(duì)話小程序
- 停水了怎么辦
- 銀耳能泡多久
- 宮燈杏仁蜜身體乳 首先…不得不吐槽一下它這個(gè)味道… 一股超級(jí)濃郁的苦杏子味兒啊!
- 正宗五香粉配方
- 對(duì)童年的回憶優(yōu)美語(yǔ)段 70后80后的回憶,太經(jīng)典了
- 致給八零后的童年經(jīng)典句子 表達(dá)70后的人生感悟
- 超市哪些是真正的酸奶
- 包 Go | 函數(shù)的使用
