后台线程使用 SynchronizationContext 更新主界面
来源:互联网 发布:手机壁纸大全软件 编辑:程序博客网 时间:2024/05/16 05:53
看过很多介绍文章介绍如何在后台线程更新主界面的,多数都是使用Control.Invoke, Control.BeginInvoke。这些都是很好的解决方案,不过有两个问题:
1. 必须的引用System.Windows.Forms,然后 using System.Windows.Forms
2. 代码结构比较零乱。(实际上这个也是#1 导致的)
M$提供了另外一个比较优雅的解决方案,就是 System.Threading. SynchronizationContext。 可以看出,它不在 namesapce System.Windows.Forms 里面,因此我们可以理直气壮地用在BusinessLaryer, Controler,甚至 module 里面。
而且它使用非常方便,只需要关注下面两个方法即可:
1. Send:发送界面更新请求至主线程,阻塞当前线程直至返回。
2. Post:发送界面更新请求至主线程,不阻塞当前线程。
实际上,他们都是同一个方法,只是 send 是同步,post 是异步
我们可以看一下下面这个例子:
点击btnThread 的时候,首先 disable btnThread,然后启动线程;接着线程主动报告进度;最后线程结束,重新 enable btnThread.
private void btnThread_Click(object sender, EventArgs e) { txtThread.Text = @"Running background thread..."; btnThread.Enabled = false; Thread t = new Thread(() => { UiState textState = new UiState { Control = txtThread, PropertyName = "Text", }; for (int i = 0; i <= 10; i++) { Thread.Sleep(new TimeSpan(0, 0, 1)); textState.PropertyValue = string.Format("Progress: {0}", i * 10); _context.Post(UpdateControl, textState); } Thread.Sleep(new TimeSpan(0, 0, 1)); UiState enableState = new UiState { Control = btnThread, PropertyName = "Enabled", PropertyValue = true, }; _context.Post(UpdateControl, enableState); } ); t.Start(); } private static void UpdateControl(object uiObject) { UiState state = uiObject as UiState; if (state == null) { throw new InvalidCastException("Cannot convert object to UiState"); } Type controlType = state.Control.GetType(); PropertyInfo property = controlType.GetProperty(state.PropertyName); property.SetValue(state.Control, state.PropertyValue, null); }
这是一个很简单的例子,也许看过的人都会说:根本就没有摆脱Control 和 From 啊!
诚然如此,为了摆脱Control,我们还需要封装一下,看下面的BackgroundWorker 类:
class BackgroundWorker { public EventHandler<EventArgs> WorkerStarted; public EventHandler<ProgressEventArgs> ReportProgress; public EventHandler<EventArgs> WorkerCompleted; public SynchronizationContext Context { get; set; } public void Start() { Thread t = new Thread(() => { Context.Post(SetStartState, null); for (int i = 0; i <= 10; i++) { Thread.Sleep(new TimeSpan(0, 0, 1)); Context.Post(UpdateProgress, i * 10); } Thread.Sleep(new TimeSpan(0, 0, 1)); Context.Post(SetCompletedState, null); } ); t.Start(); } private void SetStartState(object state) { if (WorkerStarted != null) { WorkerStarted(this, new EventArgs()); } } private void SetCompletedState(object state) { if (WorkerCompleted != null) { WorkerCompleted(this, new EventArgs()); } } private void UpdateProgress(object state) { if (ReportProgress != null) { ReportProgress(this, new ProgressEventArgs {Progress = Convert.ToInt32(state)}); } } }
有两个特别的地方:
1. 构造的时候,需要提供SynchronizationContext 实例
2. 它的三个事件(WorkerStarted,ReportProgress, WorkerCompleted),都是在 post 方法里面触发的。这样做就可以确保事件的订阅方法脱离后台线程,进入主线程。
使用的方法如下:
protected override void OnLoad(EventArgs e) { _context = SynchronizationContext.Current; } private void btnWrapper_Click(object sender, EventArgs e) { btnWrapper.Enabled = false; BackgroundWorker worker = new BackgroundWorker {Context = SynchronizationContext.Current}; worker.WorkerStarted += (o, args) => { txtWrapper.Text = "Running background thread..."; }; worker.ReportProgress += (o, args) => { txtWrapper.Text = string.Format("Progress: {0}", args.Progress); }; worker.WorkerCompleted += (o, args) => { txtWrapper.Text = "Background thread completed"; btnWrapper.Enabled = true; }; worker.Start(); }
在BackgroundWroker 方法里面,再也见不到任何与 Control, Form 有关的东西
在MainForm里面,使用的方法也非常简单。
效果图如下:
可以在这个地方下载全部代码:
https://github.com/IGabriel/CSharpForm/tree/SynchronizationContext-Sample
- 后台线程使用 SynchronizationContext 更新主界面
- 用SynchronizationContext解决界面的更新线程数据问题
- C#线程UI更新,SynchronizationContext方法
- Winform线程通信(异步更新UI--SynchronizationContext
- wxpython后台线程更新界面控件方法
- 后台线程更新界面的巧妙方法
- 利用SynchronizationContext解决界面要素的线程同步问题
- BeginInvoke的使用-1 :子线程更新主界面
- 线程之间的通讯- UI 通讯使用SynchronizationContext--(1)
- 线程之间的通讯- UI 通讯使用SynchronizationContext--(2)
- 线程通讯(SynchronizationContext )
- ios后台更新界面
- 子线程更新界面
- 线程之间的通讯---SynchronizationContext
- 线程之间的通讯---SynchronizationContext
- 线程之间的通讯---SynchronizationContext
- 线程之间的通讯---SynchronizationContext
- 线程之间的通讯---SynchronizationContext
- HDU 3081 Marriage Match II —并查集,最大流
- CoreJava 8th V-1 P33 Unicode编码
- 转 undefined reference to `ts_read_raw'
- tesseract-ocr识别中文扫描图片实例讲解
- 如何访问Google
- 后台线程使用 SynchronizationContext 更新主界面
- 《单元测试的艺术(第2版)》
- android webview 适配手机屏幕以及禁止双击事件
- linux安装编译ffmpeg
- 字符串处理: 猴子打字
- Android 二维码 生成和识别(附Demo源码)
- 多重继承下的虚函数表
- 获得时间
- Dom4j的使用(全而好的文章)