免费A级毛片无码专区网站-成人国产精品视频一区二区-啊 日出水了 用力乖乖在线-国产黑色丝袜在线观看下-天天操美女夜夜操美女-日韩网站在线观看中文字幕-AV高清hd片XXX国产-亚洲av中文字字幕乱码综合-搬开女人下面使劲插视频

一個(gè)超經(jīng)典 WinForm 卡死問題的再反思

一:背景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)而掛死
雖然知道原因,但有一個(gè)非常大的遺憾就是在 dump 中找不到到底是哪一個(gè)控件,只能籠統(tǒng)的告訴朋友,讓其洞察下代碼是哪里用了工作線程創(chuàng)建了 用戶控件, 有些朋友根據(jù)這個(gè)信息成功的找到,也有朋友因?yàn)楦鞣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ò)展閱讀