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
本文内容
简述这个线程安全问题
必要条件:
- 创建多个 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/,而 CSDN 和博客园仅从其中摘选发布,而且一旦发布了就不再更新。
如果在博客看到有任何不懂的内容,欢迎交流。我搭建了 dotnet 职业技术学院 欢迎大家加入。

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名吕毅(包含链接:https://walterlv.blog.csdn.net/),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系。
WPF 支持的多线程 UI 并不是线程安全的的更多相关文章
- WPF多线程UI更新——两种方法
WPF多线程UI更新——两种方法 前言 在WPF中,在使用多线程在后台进行计算限制的异步操作的时候,如果在后台线程中对UI进行了修改,则会出现一个错误:(调用线程无法访问此对象,因为另一个线程拥有该对 ...
- 拒绝卡顿——在WPF中使用多线程更新UI
原文:拒绝卡顿--在WPF中使用多线程更新UI 有经验的程序员们都知道:不能在UI线程上进行耗时操作,那样会造成界面卡顿,如下就是一个简单的示例: public partial class MainW ...
- WPF 多线程 UI:设计一个异步加载 UI 的容器
对于 WPF 程序,如果你有某一个 UI 控件非常复杂,很有可能会卡住主 UI,给用户软件很卡的感受.但如果此时能有一个加载动画,那么就不会感受到那么卡顿了.UI 的卡住不同于 IO 操作或者密集的 ...
- WPF 同一窗口内的多线程 UI(VisualTarget)
WPF 的 UI 逻辑只在同一个线程中,这是学习 WPF 开发中大家几乎都会学习到的经验.如果希望做不同线程的 UI,大家也会想到使用另一个窗口来实现,让每个窗口拥有自己的 UI 线程.然而,就不能让 ...
- (转).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 ...
- C#多线程之旅(3)——线程池
v博客前言 先交代下背景,写<C#多线程之旅>这个系列文章主要是因为以下几个原因:1.多线程在C/S和B/S架构中用得是非常多的;2.而且多线程的使用是非常复杂的,如果没有用好,容易造成很 ...
- 面试题_1_to_16_多线程、并发及线程的基础问题
多线程.并发及线程的基础问题 1)Java 中能创建 volatile 数组吗?能,Java 中可以创建 volatile 类型数组,不过只是一个指向数组的引用,而不是整个数组.我的意思是,如果改变引 ...
- C# 多线程的自动管理(线程池) 基于Task的方式
C# 多线程的自动管理(线程池) 在多线程的程序中,经常会出现两种情况: 1. 应用程序中线程把大部分的时间花费在等待状态,等待某个事件发生,然后给予响应.这一般使用 ThreadPool(线程 ...
- 多线程(五) java的线程锁
在多线程中,每个线程的执行顺序,是无法预测不可控制的,那么在对数据进行读写的时候便存在由于读写顺序多乱而造成数据混乱错误的可能性.那么如何控制,每个线程对于数据的读写顺序呢?这里就涉及到线程锁. 什么 ...
随机推荐
- hibernate ID生成策略配置
1.Student.hbm.xml配置 <hibernate-mapping package="com.wxh.hibernate.model"> <class ...
- mysql-幻读及其例子
第一步建表并插入5条记录: 接下来我们看下大部分mysql所说的幻读现象: 事务1(开启事务查询发现没有记录6准备插入): 事务2(开启事务,发现没有记录6插入,并提交事务): 事务1:查询发现 ...
- Android Application 类共享全局数据
android系统会为每一个程序执行时创建一个Application类的对象且仅创建一个.所以Application能够说是单例模式的一个类.且application对象的生命周期是整个程序中最长的, ...
- Centos7+httpd+fastcgi+rails安装
搭建的环境: centos7 Apache/2.4.6 fastcgi2.4.6 rails4 在安装fastcgi的时候遇到了问题: 问题: .... .. In file included fro ...
- opecv2 MeanShift 使用均值漂移算法查找物体
#if !defined OFINDER #define OFINDER #include <opencv2\core\core.hpp> #include <opencv2\img ...
- hdu_1754,线段树单点更新,求区间最值
http://www.notonlysuccess.com/index.php/segment-tree-complete/ #include<iostream> #include< ...
- [转]Zen Cart官网屏蔽中国用户访问的真正原因
近需要到 zen cart 的官方网站查询一些资料,却发现无法访问!在网上搜索一番以后,原来如此. Zen Cart官网屏蔽中国用户访问的真正原因 作者:[鹏程万里] 日期:2011-03-26 准备 ...
- BZOJ 1174: [Balkan2007]Toponyms
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 735 Solved: 102[Submit][Status][Discuss] Descriptio ...
- Android 学习笔记:Navigation Drawer
laylout文件: <android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com ...
- ipad mini2 升级9.0.2后解锁白屏解决
解锁白屏是个什么现象?就是当你用手指滑动解锁后出现输入密码的界面后,1秒之内屏幕变白,中间一个黑色的苹果,几秒之后重新回到滑动解锁的界面.我出现这个现象不是因为升级了9.0.2,而是升级了9.0.2之 ...