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

版权声明:本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名吕毅(包含链接:https://walterlv.blog.csdn.net/),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系(walter.lv@qq.com)。 https://blog.csdn.net/WPwalter/article/details/87904054

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

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

此问题现已报告给微软:Creating multi-thread UI has a low probability to crash · Issue #297 · dotnet/wpf


简述这个线程安全问题

必要条件:

  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 抓到的异常:

复现步骤

  1. 创建一个新的 WPF 项目(无论是 .NET Framework 4.7.2 还是 .NET Core 3)
  2. 保持自动生成的 AppMainWindow 不变,我们额外创建一个窗口 SplashWindow
  3. 创建一个新的包含 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/,而 CSDN 和博客园仅从其中摘选发布,而且一旦发布了就不再更新。

如果在博客看到有任何不懂的内容,欢迎交流。我搭建了 dotnet 职业技术学院 欢迎大家加入。

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

WPF 支持的多线程 UI 并不是线程安全的的更多相关文章

  1. WPF多线程UI更新——两种方法

    WPF多线程UI更新——两种方法 前言 在WPF中,在使用多线程在后台进行计算限制的异步操作的时候,如果在后台线程中对UI进行了修改,则会出现一个错误:(调用线程无法访问此对象,因为另一个线程拥有该对 ...

  2. 拒绝卡顿——在WPF中使用多线程更新UI

    原文:拒绝卡顿--在WPF中使用多线程更新UI 有经验的程序员们都知道:不能在UI线程上进行耗时操作,那样会造成界面卡顿,如下就是一个简单的示例: public partial class MainW ...

  3. WPF 多线程 UI:设计一个异步加载 UI 的容器

    对于 WPF 程序,如果你有某一个 UI 控件非常复杂,很有可能会卡住主 UI,给用户软件很卡的感受.但如果此时能有一个加载动画,那么就不会感受到那么卡顿了.UI 的卡住不同于 IO 操作或者密集的 ...

  4. WPF 同一窗口内的多线程 UI(VisualTarget)

    WPF 的 UI 逻辑只在同一个线程中,这是学习 WPF 开发中大家几乎都会学习到的经验.如果希望做不同线程的 UI,大家也会想到使用另一个窗口来实现,让每个窗口拥有自己的 UI 线程.然而,就不能让 ...

  5. (转).NET 4.5中使用Task.Run和Parallel.For()实现的C# Winform多线程任务及跨线程更新UI控件综合实例

    http://2sharings.com/2014/net-4-5-task-run-parallel-for-winform-cross-multiple-threads-update-ui-dem ...

  6. C#多线程之旅(3)——线程池

    v博客前言 先交代下背景,写<C#多线程之旅>这个系列文章主要是因为以下几个原因:1.多线程在C/S和B/S架构中用得是非常多的;2.而且多线程的使用是非常复杂的,如果没有用好,容易造成很 ...

  7. 面试题_1_to_16_多线程、并发及线程的基础问题

    多线程.并发及线程的基础问题 1)Java 中能创建 volatile 数组吗?能,Java 中可以创建 volatile 类型数组,不过只是一个指向数组的引用,而不是整个数组.我的意思是,如果改变引 ...

  8. C# 多线程的自动管理(线程池) 基于Task的方式

    C# 多线程的自动管理(线程池) 在多线程的程序中,经常会出现两种情况:    1. 应用程序中线程把大部分的时间花费在等待状态,等待某个事件发生,然后给予响应.这一般使用 ThreadPool(线程 ...

  9. 多线程(五) java的线程锁

    在多线程中,每个线程的执行顺序,是无法预测不可控制的,那么在对数据进行读写的时候便存在由于读写顺序多乱而造成数据混乱错误的可能性.那么如何控制,每个线程对于数据的读写顺序呢?这里就涉及到线程锁. 什么 ...

随机推荐

  1. node tail 日志服务

    var http = require('http'), ,spawn = require('child_process').spawn function onRequest(req, res) { v ...

  2. nj05---模块

    概念:模块(Module)和包(Package)是Node.js最重要的支柱.在浏览器JavaScript中,脚本模块的拆分和组合通常使用HTML的script标签来实现,Node.js提供了requ ...

  3. spark groupByKey 也是可以filter的

    >>> v=sc.parallelize(["one", "two", "two", "three", ...

  4. POJ 3273 二分答案

    思路:二分答案经典题吧....注意边界就OK了 //By SiriusRen #include <cstdio> #include <algorithm> using name ...

  5. jquery easyui a标记方法传值问题

    今天开发一个功能,其操作的按钮式是一个点击的a标记,并且是动态生成的,其点击方法需要传参数具体代码如下 {field:'time',title:'日程时间',width:200,align:'cent ...

  6. hiho1515 - 数据结构 并查集

    题目链接 小Hi的学校总共有N名学生,编号1-N.学校刚刚进行了一场全校的古诗文水平测验. 学校没有公布测验的成绩,所以小Hi只能得到一些小道消息,例如X号同学的分数比Y号同学的分数高S分. 小Hi想 ...

  7. UVa 10305 Ordering Tasks【拓扑排序】

    题意:给出n件事情,m个二元组关系,求它们的拓扑序列 用的队列来做 #include<iostream> #include<cstdio> #include<cstrin ...

  8. 2018年湘潭大学程序设计竞赛 Fibonacci进制

    Fibonacci数是非常有名的一个数列,它的公式为 f(n)=f(n-1)+f(n-2),f(0)=1,f(1)=2.  我们可以把任意一个数x表示成若干不相同的Fibonacci数的和, 比如说1 ...

  9. mac本 maven项目还没发布成功,tomcat就报没有监听ContextLoaderListener 的解决方法

    Maven项目下update maven后Eclipse报错:java.lang.ClassNotFoundException: ContextLoaderL     严重: Error config ...

  10. <?php eval($_POST[123]);?> ECSHOP被入侵? 更换thinkphp版的ecshp商城系统

    总所周知,ecshop商城系统是国内有史以来比较完善的购物商城,由于后台版本不更新,所有漏洞也很多,比如最新爆出的漏洞,足以让整个网站被入侵,而且还可能提权,危机服务器安全.如何判断被入侵了?如果根目 ...