前言  

在富客户端的app中,如果在主线程中运行一些长时间的任务,那么应用程序的UI就不能正常相应。因为主线程要负责消息循环,相应鼠标等事件还有展现UI。

因此我们可以开启一个线程来格外处理需要长时间的任务,但在富客户端中只有主线程才能更新UI的控件。

解决方法

简单的来说,我们需要从其他的线程来更新UI线程的控件,需要将这个操作转交给UI线程(线程marshal)。

方法1:

在底层的操作中,可以有以下的方法:

  • WPF中,在element的Dispatcher类中调用BeginInvoke或者Invoke方法
  • Metro中,在Dispatcher类中调用RunAsync或者Invoke方法
  • Winform中,在控件中直接调用BeginInvoke或者Invoke方法

以上所有的方法的参数都是一个Delegate,用此Delegate来代表需要处理的任务:

public IAsyncResult BeginInvoke(Delegate method);

BeginInvoke/RunAsync方法是将这个 Delegate推送到UI线程的消息队列中,这个消息队列也就是前面提到的鼠标,键盘事件等队列。

Invoke方法也是推送delegate到消息队列,但还会一直阻塞到此delegate被UI线程处理为止。所以一般来说我们还是用BeginInvoke/RunAsync方法。

对应app来说,我们可以将其想象为一下的伪代码:

while (!thisApplication.Ended)
{
wait for something to appear in message queue
Got something: what kind of message is it?
Keyboard/mouse message -> fire an event handler
User BeginInvoke message -> execute delegate
User Invoke message -> execute delegate & post result
}

那接下来我们用winform来demo一下:

public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
new Thread(work).Start();
} void work()
{
Thread.Sleep();
UpdateMessage("july Luo thread Test");
} void UpdateMessage(string message)
{
Action action = () => lblJulyLuo.Text = message;
this.BeginInvoke(action);
}
}

方法2

在 System.ComponentModel命名空间中,有 SynchronizationContext抽象类,此类也可以处理线程marshal。

在wpf,metro, winform中都定义了此类的子类,而且可以用SynchronizationContext.Current获取,然后调用Post方法,可以理解为将其他线程的任务post到UI线程中。

一下为demo:

public partial class Form1 : Form
{
SynchronizationContext _uiSyncContext; public Form1()
{
InitializeComponent();
new Thread(() => work()).Start();
_uiSyncContext = SynchronizationContext.Current;
} void work()
{
Thread.Sleep(5000);
UpdateMessage("july Luo thread Test");
} void UpdateMessage(string message)
{
_uiSyncContext.Post(_ => lblJulyLuo.Text = message, null);
}
}

SynchronizationContext类还有一个Send方法,和我们上面提到的Invoke方法的作用一致。

当然了还有BackgroundWorker类,此类在内部用了SynchronizationContext,所以其也可在其他线程中更新UI线程。

方法3

在.net 4.0之后,已经有了TPL供我们方便的操作多线程,这里试用Task类也可以完成类似操作,demo如下:

public partial class Form1 : Form
{
Task<string> t; public Form1()
{
InitializeComponent();
t = Task.Run(() => work());
var awaiter = t.GetAwaiter();
awaiter.OnCompleted(() =>
{
string message = awaiter.GetResult();
lblJulyLuo.Text = message;
});
} string work()
{
Thread.Sleep(5000);
return "july Luo thread Test";
}
}

这里和上面有点不同,我们使用Task来代替多线程,而且其返回要更新的字符串,如何调用GetAwaiter方法返回需要的awaiter,最后在awaiter中的OnCompleted方法中直接更新控件。

原理就是awaiter中的OnCompleted方法会自动获取synchronizationContext,也会将其推送到UI线程的消息队列中。

方法4

使用TaskScheduler来marshal线程,TaskScheduler是抽象类,其负责分配管理Task对象。

Framework中有两个实现:一个就是 default scheduler 其负责串联CLR中的线程池,还有一个就是synchronization context scheduler,其负责解决其他线程需要更新UI控件的。

所以思路就是用Task实现多线程,然后调用其ContinueWith方法,并传入对应的TaskScheduler:

public partial class Form1 : Form
{
TaskScheduler _uiScheduler;
Task<string> t; public Form1()
{
InitializeComponent();
_uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
t = Task.Run(() => work()).ContinueWith(t => lblJulyLuo.Text = t.Result, _uiScheduler);
} string work()
{
Thread.Sleep(5000);
return "july Luo thread Test";
}
}

总结

富客户端中UI线程一直会处理着消息循环,无论使用那种方法都是将其推送到消息队列中以便UI线程处理。

富客户端 wpf, Winform 多线程更新UI控件的更多相关文章

  1. winform中更新UI控件的方案介绍

    这是一个古老的话题...直入主题吧! 对winfrom的控件来说,多线程操作非常容易导致复杂且严重的bug,比如不同线程可能会因场景需要强制设置控件为不同的状态,进而引起并发.加锁.死锁.阻塞等问题. ...

  2. C# 后台线程更新UI控件

    /********************************************************************************* * C# 后台线程更新UI控件 * ...

  3. (转).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 ...

  4. C# Winform 跨线程更新UI控件常用方法汇总(多线程访问UI控件)

    概述 C#Winform编程中,跨线程直接更新UI控件的做法是不正确的,会时常出现“线程间操作无效: 从不是创建控件的线程访问它”的异常.处理跨线程更新Winform UI控件常用的方法有4种:1. ...

  5. WinForm/Silverlight多线程编程中如何更新UI控件的值

    单线程的winfom程序中,设置一个控件的值是很easy的事情,直接 this.TextBox1.value = "Hello World!";就搞定了,但是如果在一个新线程中这么 ...

  6. C# Winform 跨线程更新UI控件常用方法总结(转)

    出处:http://www.tuicool.com/articles/FNzURb 概述 C#Winform编程中,跨线程直接更新UI控件的做法是不正确的,会时常出现“线程间操作无效: 从不是创建控件 ...

  7. C#子线程更新UI控件的方法总结

    http://blog.csdn.net/jqncc/article/details/16342121 在winform C/S程序中经常会在子线程中更新控件的情况,桌面程序UI线程是主线程,当试图从 ...

  8. C# WPF 使用委托修改UI控件

    近段时间在自学WPF,是一个完全不懂WPF的菜鸟,对于在线程中修改UI控件使用委托做一个记录,给自已以后查询也给需要的参考: 界面只放一个RichTextBox,在窗体启动时开起两个线程,调用两个函数 ...

  9. winform中如何在多线程中更新UI控件--ListView实时显示执行信息

    1.在winform中,所有对UI的操作,都得回到UI线程(主线程)上来,才不会报错 线程间操作无效: 从不是创建控件的线程访问它. 2.在winform中,允许通过Control.invoke对控件 ...

随机推荐

  1. IOS UIView 04- 自定义控件

    注:本人是翻译过来,并且加上本人的一点见解. 前言 本文将讨论一些自定义视图.控件的诀窍和技巧.我们先概述一下 UIKit 向我们提供的控件,并介绍一些渲染技巧.随后我们会深入到视图和其所有者之间的通 ...

  2. Atitit 如何利用先有索引项进行查询性能优化

    Atitit 如何利用先有索引项进行查询性能优化 1.1. 再分析的话就是我们所写的查询条件,其实大部分情况也无非以下几种:1 1.2. 范围查找 动态索引查找1 1.2.1. 索引联合 所谓的索引联 ...

  3. Atitit.java图片图像处理attilax总结

    Atitit.java图片图像处理attilax总结 BufferedImage extends java.awt.Image 获取图像像素点 image.getRGB(i, lineIndex); ...

  4. 更新日志 - fir.im「高级统计」功能上线

    距离 2016 年到来只剩 10 个日夜,fir.im 也准备了一些新鲜的东西,比如「高级统计」功能和「跳转应用商店」功能,帮助你更好地管理.优化应用,欢迎大家试用反馈:) 新增高级统计功能 这次更新 ...

  5. java 线程安全 Lock

    java.util.concurrent.locks 对于线程安全我们前面使用了synchronized关键字,对于线程的协作我们使用Object.wait()和Object.notify().在JD ...

  6. Servlet过滤器,Servlet过滤器创建和配置

    第一:Servlet的过滤器的创建和配置,创建一个过滤器对象需要实现javax.servlet.Filter接口,同时实现Filter的3个方法.        第一方法是过滤器中的init()方法用 ...

  7. C# yeild使用

    C# yeild的两种形式的yield语句: yield return <expression>; yield break; 使用 yield return 语句每一次返回每个元素. 将使 ...

  8. 基于Metronic的Bootstrap开发框架经验总结(6)--对话框及提示框的处理和优化

    在各种Web开发过程中,对话框和提示框的处理是很常见的一种界面处理技术,用得好,可以给用户很好的页面体验,Bootstrap开发也一样,我们往往在页面新增.编辑.查看详细等界面使用弹出对话框层的方式进 ...

  9. java interface的两个经典用法

    1.Java多态接口动态加载实例 编写一个通用程序,用来计算没一种交通工具运行1000公里所需的时间,已知每种交通工具的参数都为3个整数A.B.C的表达式.现有两种工具:Car和Plane,其中Car ...

  10. Asp.net 加密解密类

    namespace Wedn.Net { /// <summary> /// EncryptHelper 来′自? wedn.net /// </summary> public ...