一:背景1.讲故事【一个超经典 WinForm 卡死问题的再反思】这篇文章起源于昨天的一位朋友发给我的dump文件 , 说它的程序出现了卡死,看了下程序的主线程栈,居然又碰到了 OnUserPreferenceChanged
导致的挂死问题,真的是经典中的经典,线程栈如下:
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)...
说实话,这种dump从去年看到今年,应该不下五次了,都看烦了,其形成原因是:
- 未在主线程中生成用户控件,导致用 WindowsFormsSynchronizationContext.Send 跨线程封送时,对方无法响应请求进而挂死
为了不让这些朋友的遗憾延续下去 , 这一篇做一个系统归纳,希望能助这些朋友一臂之力 。
二:解决方案1. 背景这个问题的形成详情,我在去年的一篇文章为:记一次 .NET 某新能源汽车锂电池检测程序 UI挂死分析
https://www.cnblogs.com/huangxincheng/p/15245554.html
中已经做过分享,因为 dump 中找不到问题的 Control,所以也留下了一些遗憾,这一篇就做个补充 。2. 问题突破点分析熟悉 WinForm 底层的朋友应该知道 , 一旦在 工作线程 上创建了 Control 控件,框架会自动给这个线程配备一个
WindowsFormsSynchronizationContext
和其底层的 MarshalingControl
,这个是有源码支撑的,大家可以找下 Control 的构造函数,简化后的源码如下: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;}}}}
这段代码可以挖到下面两点信息 。- 一旦 Control 创建在工作线程上,那这个线程就会安装一个 WindowsFormsSynchronizationContext 变量 , 比如此时就存在两个对象了 。
0:000:x86> !dsoOS Thread Id: 0x4eb688 (0)ESP/REGObjectName002FEC40 025a0fb0 System.Windows.Forms.WindowsFormsSynchronizationContext...002FEF44 0260992c System.Object[](System.Object[])002FEF48 02d69164 System.Windows.Forms.WindowsFormsSynchronizationContext...
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 同一个wifi怎么联机玩红警(win10红警2局域网联机)
- 【炫丽】从0开始做一个WPF+Blazor对话小程序
- 宫灯杏仁蜜身体乳 首先…不得不吐槽一下它这个味道… 一股超级浓郁的苦杏子味儿啊!
- 包 Go | 函数的使用
- 怎么下载ps电脑永久免费版(一个正版的ps要多少钱)
- 简易版 纯css爱心代码-最近超级火的打火机与公主裙中的爱心代码
- 游戏工作室到底是怎么赚钱的(做一个工作室赚钱吗)
- 安卓版经典galgame打包下载,跪求《安卓galgame经典合集》
- 琉璃神社,东西怎么下载,就给了一个/后面就10个字母这是什么东西呀,怎么用它下载呀
- Java 超新星开源项目 Solon v1.10.10 发布