是什么让代码在一个WPF GUI线程中执行的首选方式? [英] What is the preferred way to get code to execute in a WPF GUI thread?

查看:206
本文介绍了是什么让代码在一个WPF GUI线程中执行的首选方式?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个包含我的WPF图形用户界面和一个或偶尔需要在主线程异步执行代码的后台线程(例如,在GUI中的状态更新)。


$ B一个主线程$ b

有两种方式(即我所知道的,也许更多)来实现:




  1. 使用计划任务的TaskScheduler 从目标线程的同步上下文和

  2. 通过调用使用调度<委托/ code>从目标线



在代码:



<预类= 郎咸平-CS prettyprint-覆盖> 使用System.Threading.Tasks;
使用的System.Threading;

行动mainAction =()=> MessageBox.Show(的String.Format(你好,从线程{0},Thread.CurrentThread.ManagedThreadId));
行动backgroundAction;

//执行在主线程直接(对于verifiying线程ID)
mainAction();

//通过的TaskScheduler
变种的TaskScheduler =执行主线程TaskScheduler.FromCurrentSynchronizationContext();
backgroundAction =()=> Task.Factory.StartNew(mainAction,CancellationToken.None,TaskCreationOptions.None,的TaskScheduler);
Task.Factory.StartNew(backgroundAction);

//通过调度
无功调度= System.Windows.Threading.Dispatcher.CurrentDispatcher执行在主线程;
backgroundAction =()=> dispatcher.BeginInvoke(mainAction);
Task.Factory.StartNew(backgroundAction);



我喜欢基于TPL-版本,因为我已经使用TPL了很多,它为我提供了一个很大的灵活性,例如,通过等待的任务,或与其他的任务链接它。然而,在WPF代码的大多数例子我迄今所看到的,使用了调度变体。



假设我不需要任何的灵活性,只想要执行一些代码在目标线,是什么人宁愿一种方式比其他的原因?是否有任何性能影响?


解决方案

我强烈建议您阅读的基于任务的异步模式文件。这将允许你组织你的API,以准备当异步等待上街游行。



我用的TaskScheduler 来排队的更新,类似的解决方案(的博客文章),但我不再推荐这种方法。



的TAP文档有一个简单的解决方案,更优雅的解决了这个问题:如果一个后台操作要发布的进度报告,那么它需要类型的参数 IProgress< T>

 公共接口IProgress<在T> {无效报告(T值); } 

这则相对简单,提供了一个基本的实现:

 公共密封类EventProgress< T> :IProgress< T> 
{
私人只读的SynchronizationContext syncContext;

公共EventProgress()
{
this.syncContext = SynchronizationContext.Current?新的SynchronizationContext();
}

公共事件动作< T>进展;

无效IProgress< T>。报道(T值)
{
this.syncContext.Post(_ =>
{
如果(这.Progress = NULL)
this.Progress(值);!
},NULL);
}
}



SynchronizationContext.Current 本质上是 TaskScheduler.FromCurrentSynchronizationContext ,而不需要实际的工作 S)。



异步CTP中包含 IProgress< T> 进度< T> 这是类似于 EventProgress<型; T> 以上(但更好的性能)。如果你不想安装CTP层次的东西,那么你可以使用上面的类型



要总结一下,真的有四个选项:




  1. IProgress< T> - 这是未来的路异步代码将被写入。这也迫使你分开你的后台操作逻辑与的你的 UI /视图模型更新代码的,这是一件好事。

  2. 的TaskScheduler - 个不错的办法;这是我用了很长的时间切换到 IProgress<之前,T> 。它不强制用户界面/视图模型更新代码进行后台操作的逻辑,虽然

  3. 的SynchronizationContext - 同样的优点和缺点到的TaskScheduler ,通过一个鲜为人知的API

  4. 调度 - 真的可以的不可以推荐这个!考虑后台操作更新视图模型 - 所以没有什么具体的UI,在进度更新代码。在这种情况下,使用调度只是绑住你的视图模型到你的UI平台。讨厌的。



P.S。如果你选择使用异步CTP,那么我有一些额外的 IProgress< T> 在我的Nito.AsyncEx库,其中一个( PropertyProgress )通过 INotifyPropertyChanged的发送进度报告


(通过的SynchronizationContext 切换回UI线程后)

I have a main thread that contains my WPF GUI and one or more background threads that occassionally need to execute code in the main thread asynchronously (e.g. a status update in the GUI).

There are two ways (that I know of, maybe more) to accomplish this:

  1. by scheduling a task using the TaskScheduler from the target thread's synchronization context, and
  2. by invoking a delegate using the Dispatcher from the target thread.

In code:

using System.Threading.Tasks;
using System.Threading;

Action mainAction = () => MessageBox.Show(string.Format("Hello from thread {0}", Thread.CurrentThread.ManagedThreadId));
Action backgroundAction;

// Execute in main thread directly (for verifiying the thread ID)
mainAction();

// Execute in main thread via TaskScheduler
var taskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
backgroundAction = () => Task.Factory.StartNew(mainAction, CancellationToken.None, TaskCreationOptions.None, taskScheduler);
Task.Factory.StartNew(backgroundAction);

// Execute in main thread via Dispatcher
var dispatcher = System.Windows.Threading.Dispatcher.CurrentDispatcher;
backgroundAction = () => dispatcher.BeginInvoke(mainAction);
Task.Factory.StartNew(backgroundAction);

I like the TPL-based version, because I already use TPL a lot and it provides me with a lot of flexibility, e.g. by waiting on the task or chaining it with other tasks. However, in most examples of WPF code I have seen so far, the Dispatcher variant was used.

Assuming I didn't need any of that flexibility and only wanted to execute some code in the target thread, what are the reasons one would prefer one way over the other? Are there any performance implications?

解决方案

I highly recommend that you read the Task-based Asynchronous Pattern document. This will allow you to structure your APIs to be ready when async and await hit the streets.

I used to use TaskScheduler to queue updates, similar to your solution (blog post), but I no longer recommend that approach.

The TAP document has a simple solution that solves the problem more elegantly: if a background operation wants to issue progress reports, then it takes an argument of type IProgress<T>:

public interface IProgress<in T> { void Report(T value); }

It's then relatively simple to provide a basic implementation:

public sealed class EventProgress<T> : IProgress<T>
{
  private readonly SynchronizationContext syncContext;

  public EventProgress()
  {
    this.syncContext = SynchronizationContext.Current ?? new SynchronizationContext();
  }

  public event Action<T> Progress;

  void IProgress<T>.Report(T value)
  {
    this.syncContext.Post(_ =>
    {
      if (this.Progress != null)
        this.Progress(value);
    }, null);
  }
}

(SynchronizationContext.Current is essentially TaskScheduler.FromCurrentSynchronizationContext without the need for actual Tasks).

The Async CTP contains IProgress<T> and a Progress<T> type that is similar to the EventProgress<T> above (but more performant). If you don't want to install CTP-level stuff, then you can just use the types above.

To summarize, there are really four options:

  1. IProgress<T> - this is the way asynchronous code in the future will be written. It also forces you to separate your background operation logic from your UI/ViewModel update code, which is a Good Thing.
  2. TaskScheduler - not a bad approach; it's what I used for a long time before switching to IProgress<T>. It doesn't force the UI/ViewModel update code out of the background operation logic, though.
  3. SynchronizationContext - same advantages and disadvantages to TaskScheduler, via a lesser-known API.
  4. Dispatcher - really can not recommend this! Consider background operations updating a ViewModel - so there's nothing UI-specific in the progress update code. In this case, using Dispatcher just tied your ViewModel to your UI platform. Nasty.

P.S. If you do choose to use the Async CTP, then I have a few additional IProgress<T> implementations in my Nito.AsyncEx library, including one (PropertyProgress) that sends the progress reports through INotifyPropertyChanged (after switching back to the UI thread via SynchronizationContext).

这篇关于是什么让代码在一个WPF GUI线程中执行的首选方式?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆