博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
WPF多线程UI更新——两种方法
阅读量:4977 次
发布时间:2019-06-12

本文共 3712 字,大约阅读时间需要 12 分钟。

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

前言

  在WPF中,在使用多线程在后台进行计算限制的异步操作的时候,如果在后台线程中对UI进行了修改,则会出现一个错误:(调用线程无法访问此对象,因为另一个线程拥有该对象。)这是很常见的一个错误,一不小心就会有这个现象。在WPF中,如果不是用多线程的话,例如单线程应用程序,就是说代码一路过去都在GUI线程运行,可以随意更新任何东西,包括UI对象。但是使用多线程来更新UI就可能会出现以上所说问题,怎么解决?本文章提供两个方法:Dispatcher(大部分人使用),TaskScheduler(任务调度器)。

 

问题再现

  可能有的WPF新手不懂这是什么情况,先来个问题的再现,再使用本文章的两个方法进行解决。

  为了演示方便,我使用了最简单的布局,一个开始按钮,三个TextBlock。按一下开始按钮,开一个后台线程随机得到一个数字,并且更新第一个TextBlock。再开另外一个后台线程得到另外一个数字,更新第二个TextBlock。第三个TextBlock处理同理。

  XAML代码:

  后台代码:

public partial class MainWindow : Window    {        public MainWindow()        {            InitializeComponent();        }        private void Button_Click(object sender, RoutedEventArgs e)        {            Task.Factory.StartNew(Work);        }        private void Work()        {            Task task = new Task((tb) => Begin(this.first), this.first);            Task task2 = new Task((tb) => Begin(this.second), this.first);            Task task3 = new Task((tb) => Begin(this.Three), this.first);            task.Start();            task.Wait();            task2.Start();            task2.Wait();            task3.Start();        }        private void Begin(TextBlock tb)        {            int i=100000000;            while (i>0)            {                i--;            }            Random random = new Random();            String Num = random.Next(0, 100).ToString();            tb.Text = Num;        }    }

    运行一下,在点击开始按钮的时候,得到了一个错误信息:

  果然不出所料,Begin函数是在后台线程执行的,tb这个TextBlock是前台UI线程的对象,所以无法在后台线程改变UI线程拥有的对象,很多有点经验的WPF程序员就会使用下面我要说的Dispatcher了!

 

问题解决

  方法一:Dispatcher

    1.把UI更新的代码放到一个函数中:

private void UpdateTb(TextBlock tb, string text)        {            tb.Text = text;        }

    2.使用Dispatcher,大家看修改后的Begin函数(红色内容):

private void Begin(TextBlock tb)        {            int i=100000000;            while (i>0)            {                i--;            }            Random random = new Random();            String Num = random.Next(0, 100).ToString();            Action
updateAction = new Action
(UpdateTb); tb.Dispatcher.BeginInvoke(updateAction,tb,Num); }

  再运行一次程序,可以看到能正常显示了,并且不会出现假死现象。

  方法二:任务调度器(TaskScheduler)

    有很多任务调度器,在CLR Var C#中就提出了线程池任务调度器,I/O任务调度器,任务限时调度器等,调度器的职责就是负责任务的调度,调节任务执行。同步上下文任务调度器就是该方法二所使用的调度器,其作用是将所有任务都调度给应用程序的GUI线程。

public partial class MainWindow : Window    {        public MainWindow()        {            InitializeComponent();        }        private readonly TaskScheduler _syncContextTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();        private void Button_Click(object sender, RoutedEventArgs e)        {            Task.Factory.StartNew(SchedulerWork);        }        private void SchedulerWork()        {            Task.Factory.StartNew(Begin, this.first).Wait();            Task.Factory.StartNew(Begin, this.second).Wait();            Task.Factory.StartNew(Begin, this.Three).Wait();        }        private void Begin(object obj)        {            TextBlock tb = obj as TextBlock;            int i = 100000000;            while (i>0)            {                i--;            }            Random random = new Random();            String Num = random.Next(0,100).ToString();            Task.Factory.StartNew(() => UpdateTb(tb, Num),                    new CancellationTokenSource().Token, TaskCreationOptions.None, _syncContextTaskScheduler).Wait();        }        private void UpdateTb(TextBlock tb, string text)        {            tb.Text = text;        }    }

 

   结果展示:

    

总结

  任务调度器还有很多种,按照自己喜欢的方法来实现后台多线程更新UI。还有任务调度器也可以应用到Winform中。下面提供示例Demo下载。

                                   

转载于:https://www.cnblogs.com/Jarvin/p/3756061.html

你可能感兴趣的文章
Codeforces Round #413 A. Carrot Cakes
查看>>
Linux(Ubuntu16.04)下添加新用户
查看>>
Windows c++应用程序通用日志组件(组件及测试程序下载)
查看>>
openstack dpdk
查看>>
springmvc跳转方式
查看>>
Linux安装Redis
查看>>
IOS 第三方管理库管理 CocoaPods
查看>>
背景色渐变(兼容各浏览器)
查看>>
Redis中7种集合类型应用场景
查看>>
MariaDB 和 MySQL 比较
查看>>
MYSQL: 1292 - Truncated incorrect DOUBLE value: '184B3C0A-C411-47F7-BE45-CE7C0818F420'
查看>>
Java JPA @Transient 在Hibernate中应用
查看>>
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
查看>>
springMVC Controller 参数映射
查看>>
JDK1.8源码分析02之阅读源码顺序
查看>>
java使用jsp servlet来防止csrf 攻击的实现方法
查看>>
【bzoj题解】2186 莎拉公主的困惑
查看>>
Protocol Buffer学习笔记
查看>>
Update 语句
查看>>
HBuilder打包Android apk 支付不了问题解决
查看>>