内容简介:WPF 支持创建多个 UI 线程,跨窗口的或者窗口内的都是可以的;但是这个过程并不是线程安全的。你有极低的概率会遇到 WPF 多线程 UI 的线程安全问题,说直接点就是崩溃。本文将讲述其线程安全问题。必要条件:
WPF 支持创建多个 UI 线程,跨窗口的或者窗口内的都是可以的;但是这个过程并不是线程安全的。
你有极低的概率会遇到 WPF 多线程 UI 的线程安全问题,说直接点就是崩溃。本文将讲述其线程安全问题。
简述这个线程安全问题
必要条件:
- 创建多个 WPF UI 线程
- 其实两个就够了,一个我们平时写的 App 类所在的主 UI 线程;一个后台 UI 线程,例如用来显示启动闪屏的 UI 线程
- 两个线程的话你需要大量重复试验才能复现;而创建更多线程可以大大提高单次复现概率
- 这些 UI 线程都显示 WPF 窗口
- 无论是 .NET Framework 4.7.2 版本的 WPF,还是 .NET Core 3 版本的 WPF 都会出现此问题
现象:
- 抛出异常,程序崩溃
比如下面是其中一种异常:
Exception thrown: 'System.NullReferenceException' in WindowsBase.dll Object reference not set to an instance of an object. System.NullReferenceException: Object reference not set to an instance of an object. at System.IO.Packaging.PackagePart.CleanUpRequestedStreamsList() at System.IO.Packaging.PackagePart.GetStream(FileMode mode, FileAccess access) at System.Windows.Application.LoadComponent(Object component, Uri resourceLocator) at Walterlv.Bugs.MultiThreadedUI.SplashWindow.InitializeComponent() in C:\Users\lvyi\Desktop\Walterlv.Bugs.MultiThreadedUI\Walterlv.Bugs.MultiThreadedUI\SplashWindow.xaml:line 1 at Walterlv.Bugs.MultiThreadedUI.SplashWindow..ctor() in C:\Users\lvyi\Desktop\Walterlv.Bugs.MultiThreadedUI\Walterlv.Bugs.MultiThreadedUI\SplashWindow.xaml.cs:line 24 at Walterlv.Bugs.MultiThreadedUI.Program.<>c__DisplayClass1_0.<RunSplashWindow>b__0() in C:\Users\lvyi\Desktop\Walterlv.Bugs.MultiThreadedUI\Walterlv.Bugs.MultiThreadedUI\Program.cs:line 33
下图是 .NET Core 3 版本的 WPF 中在 Visual Studio 2019 抓到的异常:
复现步骤
- 创建一个新的 WPF 项目(无论是 .NET Framework 4.7.2 还是 .NET Core 3)
- 保持自动生成的
App
和MainWindow
不变,我们额外创建一个窗口SplashWindow
。 - 创建一个新的包含 Main 函数的
Program
类,并在项目属性中设置Program
为启动对象(替代App
)。
其他文件全部保持 Visual Studio 生成的默认代码不变,而 Program.cs 的代码如下:
using System; using System.Threading; using System.Windows.Threading; namespace Walterlv.Bugs.MultiThreadedUI { public class Program { [STAThread] private static void Main(string[] args) { for (var i = 0; i < 50; i++) { RunSplashWindow(i); } var app = new App(); app.InitializeComponent(); app.Run(); } private static void RunSplashWindow(int index) { var thread = new Thread(() => { var window = new SplashWindow { Title = $"SplashWindow {index.ToString().PadLeft(2, ' ')}", }; window.Show(); Dispatcher.Run(); }) { IsBackground = true, }; thread.SetApartmentState(ApartmentState.STA); thread.Start(); } } }
说明:即便在 new SplashWindow
代码之前调用以下方法修改 SynchronizationContext
也依然会发生异常。
SynchronizationContext.SetSynchronizationContext( new DispatcherSynchronizationContext( Dispatcher.CurrentDispatcher));
本文会经常更新,请阅读原文: https://walterlv.com/post/wpf-multi-thread-ui-is-not-thread-safe.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名 吕毅 (包含链接:https://walterlv.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系 (walter.lv@qq.com) 。
以上所述就是小编给大家介绍的《WPF 支持的多线程 UI 并不是线程安全的》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- Lua 的多线程支持
- 风铃虫 2.1.0 发布,支持多任务多线程
- Redis 6.0 稳定版发布,支持多线程 IO
- JeeSite V4.1.4 发布,支持分片上传、多线程上传
- Kitty 中的动态线程池支持 Nacos、Apollo 多配置中心了
- Kitty中的动态线程池支持Nacos,Apollo多配置中心了
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。