WPF 支持的多线程 UI 并不是线程安全的

栏目: ASP.NET · 发布时间: 5年前

内容简介:WPF 支持创建多个 UI 线程,跨窗口的或者窗口内的都是可以的;但是这个过程并不是线程安全的。你有极低的概率会遇到 WPF 多线程 UI 的线程安全问题,说直接点就是崩溃。本文将讲述其线程安全问题。必要条件:

WPF 支持创建多个 UI 线程,跨窗口的或者窗口内的都是可以的;但是这个过程并不是线程安全的。

你有极低的概率会遇到 WPF 多线程 UI 的线程安全问题,说直接点就是崩溃。本文将讲述其线程安全问题。

简述这个线程安全问题

必要条件:

  1. 创建多个 WPF UI 线程
    • 其实两个就够了,一个我们平时写的 App 类所在的主 UI 线程;一个后台 UI 线程,例如用来显示启动闪屏的 UI 线程
    • 两个线程的话你需要大量重复试验才能复现;而创建更多线程可以大大提高单次复现概率
  2. 这些 UI 线程都显示 WPF 窗口
  3. 无论是 .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 支持的多线程 UI 并不是线程安全的

复现步骤

  1. 创建一个新的 WPF 项目(无论是 .NET Framework 4.7.2 还是 .NET Core 3)
  2. 保持自动生成的 AppMainWindow 不变,我们额外创建一个窗口 SplashWindow
  3. 创建一个新的包含 Main 函数的 Program 类,并在项目属性中设置 Program 为启动对象(替代 App )。

WPF 支持的多线程 UI 并不是线程安全的

其他文件全部保持 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 ,以避免陈旧错误知识的误导,同时有更好的阅读体验。

WPF 支持的多线程 UI 并不是线程安全的 本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名 吕毅 (包含链接:https://walterlv.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系 (walter.lv@qq.com)


以上所述就是小编给大家介绍的《WPF 支持的多线程 UI 并不是线程安全的》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

代码之髓

代码之髓

[日] 西尾泰和 / 曾一鸣 / 人民邮电出版社 / 2014-8 / 45.00元

《代码之髓:编程语言核心概念》作者从编程语言设计的角度出发,围绕语言中共通或特有的核心概念,通过语言演变过程中的纵向比较和在多门语言中的横向比较,清晰地呈现了程序设计语言中函数、类型、作用域、类、继承等核心知识。本书旨在帮助读者更好地理解各种概念是因何而起,并在此基础上更好地判断为何使用、何时使用及怎样使用。同时,在阅读本书后,读者对今后不断出现的新概念的理解能力也将得到提升。 《代码之髓:......一起来看看 《代码之髓》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

MD5 加密
MD5 加密

MD5 加密工具

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具