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

前言

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

问题再现

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

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

  XAML代码:

<Window x:Class="UpdateUIDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="" Width="">
<Canvas>
<TextBlock Width="" Canvas.Left="" Canvas.Top="" Height="" x:Name="first" Background="Black" Foreground="White"></TextBlock>
<TextBlock Width="" Canvas.Left="" Canvas.Top="" Height="" x:Name="second" Background="Black" Foreground="White"></TextBlock>
<TextBlock Width="" Canvas.Left="" Canvas.Top="" Height="" x:Name="Three" Background="Black" Foreground="White"></TextBlock>
<Button Height="" Width="" Canvas.Left="" Canvas.Top="" Content="开始" Click="Button_Click"></Button>
</Canvas>
</Window>

  后台代码:

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=;
while (i>)
{
i--;
}
Random random = new Random();
String Num = random.Next(, ).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=;
while (i>)
{
i--;
}
Random random = new Random();
String Num = random.Next(, ).ToString();
Action<TextBlock, String> updateAction = new Action<TextBlock, string>(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 = ;
while (i>)
{
i--;
}
Random random = new Random();
String Num = random.Next(,).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下载。

完整Demo下载

WPF多线程UI更新——两种方法的更多相关文章

  1. 我的Android最佳实践之—— Android更新UI的两种方法:handler与runOnUiThread()

    在Android开发过程中,常需要更新界面的UI.而更新UI是要主线程来更新的,即UI线程更新.如果在主线线程之外的线程中直接更新页面 显示常会报错.抛出异常:android.view.ViewRoo ...

  2. Android更新UI的两种方法——handler与runOnUiThread()

    在Android开发过程中,常需要更新界面的UI.而更新UI是要主线程来更新的,即UI线程更新.如果在主线线程之外的线程中直接更新页面 显示常会报错.抛出异常:android.view.ViewRoo ...

  3. WPF多线程UI更新

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

  4. iOS子线程更新UI的两种方法

    http://blog.csdn.net/libaineu2004/article/details/45368427 方法1:performSelectorOnMainThread[self perf ...

  5. Android 更新UI的两种方法——handler和runOnUiThread()

    今天看到了一个runOnUiThread()方法用来更新UI,觉得很神奇!! 方法一:handler机制不说了. 方法二:利用Activity.runOnUiThread(Runnable)把更新ui ...

  6. Java-将多线程停止的两种方法

    线程如何停止呢 stop方法过时了,看起描述发现,有其他解决方案. 线程结束:就是让线程任务代码执行完,run方法结束. run方法怎么结束呢? run方法中通常都定义循环,只要控制住循环就哦了. / ...

  7. Android更新UI的几种方法

    在Android开发过程中,常需要更新界面的UI.比如网络请求操作.一些耗时操作都不能放在UI线程中运行的,需要放在子线程,而子线程又不能更新UI界面,这是我们需要引入一个Handler,消息处理机制 ...

  8. WPF程序将DLL嵌入到EXE的两种方法

    WPF程序将DLL嵌入到EXE的两种方法 这一篇可以看作是<Visual Studio 版本转换工具WPF版开源了>的续,关于<Visual Studio 版本转换工具WPF版开源了 ...

  9. Android 更新UI的两个方法

    Android 更新UI的两个方法 在Android的开发过程中,常常需要适时的更新UI.Androd中的UI是在主线程中更新的.如果在主线程之外的线程中直接更新,就会出现报错并抛出异常: andro ...

随机推荐

  1. java设计模式类图大全

    近来在看书实现GoF的23个设计模式,自己一点点地用建模工具按照自己的理解画出类图(是比较符合我个人思考理解的,个人觉得比通用类图更详细些),碰巧找到了一个挺好用的UML建模工具StarUML,也刚好 ...

  2. hdu 3487 Play with Chain

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3487 YaoYao is fond of playing his chains. He has a c ...

  3. map初始化定时器

    init_timer(); //各种定时器的初始化 void Map::init_timer() { //auto tf = GetPlug(TimerFactory); auto tf = m_sp ...

  4. HDOJ 1466 计算直线的交点数

    将n 条直线排成一个序列,直线2和直线1最多只有一个交点,直线3和直线1,2最多有两个交点,......,直线n 和其他n-1条直线最多有n-1个交点.由此得出n条直线互不平行且无三线共点的最多交点数 ...

  5. Apache CXF实现Web Service(3)——Tomcat容器和不借助Spring的普通Servlet实现JAX-RS(RESTful) web service

    起步 参照这一系列的另外一篇文章: Apache CXF实现Web Service(2)——不借助重量级Web容器和Spring实现一个纯的JAX-RS(RESTful) web service 首先 ...

  6. spring mvc Controller与jquery Form表单提交代码demo

    1.JSP表单 <% String basePath = request.getScheme() + "://" + request.getServerName() +&qu ...

  7. 流程控制语句和增强for循环

    import java.lang.Math; //import java.util.Arrays; public class test{ public static void main(String[ ...

  8. Too many levels of symbolic links 问题

    Too many levels of symbolic links 问题 Posted on 2011-11-30 20:33 张贺 阅读(5826) 评论(0) 编辑 收藏 今天弄了个ZendStu ...

  9. 用于主题检测的临时日志(b42e98ba-eb4f-4099-a54c-7aee3f29c3dd - 3bfe001a-32de-4114-a6b4-4005b770f6d7)

    这是一个未删除的临时日志.请手动删除它.(184c28c9-c88e-48fe-9713-6891e2d15044 - 3bfe001a-32de-4114-a6b4-4005b770f6d7)

  10. 就地交叉数组元素[a1a2b1b2]->[a1b1a2b2]

    问题描述: If [a1,a2,a3...,an,b1,b2...bn] is given input change this to [a1,b1,a2,b2.....an,bn] , solutio ...