获取代码在WPF GUI线程中执行的首选方法是什么?

Per*_*xus 2 c# wpf task-parallel-library

我有一个主线程,包含我的WPF GUI和一个或多个后台线程,偶尔需要异步执行主线程中的代码(例如GUI中的状态更新).

有两种方法(我知道,可能更多)来实现这一目标:

  1. 通过使用TaskScheduler目标线程的同步上下文来调度任务,以及
  2. 通过使用Dispatcher目标线程调用委托.

在代码中:

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);
Run Code Online (Sandbox Code Playgroud)

我喜欢基于TPL的版本,因为我已经使用了很多TPL,它为我提供了很多灵活性,例如通过等待任务或将其与其他任务联系起来.但是,在迄今为止我看到的WPF代码的大多数示例中,使用了Dispatcher变体.

假设我不需要任何灵活性并且只想在目标线程中执行一些代码,那么一个人比另一个更喜欢一种方式的原因是什么?是否有任何性能影响?

Ste*_*ary 9

我强烈建议您阅读基于任务的异步模式文档.这将允许你组织你的API来准备时async,并await上街游行.

我曾经使用TaskScheduler排队更新,类似于你的解决方案(博客文章),但我不再推荐这种方法.

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

public interface IProgress<in T> { void Report(T value); }
Run Code Online (Sandbox Code Playgroud)

然后提供基本实现相对简单:

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);
  }
}
Run Code Online (Sandbox Code Playgroud)

(SynchronizationContext.Current基本上TaskScheduler.FromCurrentSynchronizationContext不需要实际的Tasks).

Async CTP包含IProgress<T>Progress<T>类似于EventProgress<T>上面的类型(但性能更高).如果您不想安装CTP级别的东西,那么您可以使用上面的类型.

总而言之,有四种选择:

  1. IProgress<T> - 这是将来编写异步代码的方式.它还会强制您将后台操作逻辑UI/ViewModel更新代码分开,这是一件好事.
  2. TaskScheduler - 不是一个糟糕的方法; 这是我在切换之前使用了很长时间IProgress<T>.但是,它不会强制UI/ViewModel更新代码超出后台操作逻辑.
  3. SynchronizationContext- TaskScheduler通过鲜为人知的API具有相同的优点和缺点.
  4. Dispatcher- 真的不能推荐这个!考虑更新ViewModel的后台操作 - 因此进度更新代码中没有特定于UI的操作.在这种情况下,Dispatcher只需将ViewModel绑定到UI平台即可.讨厌.

PS如果您确实选择使用Async CTP,那么IProgress<T>我的Nito.AsyncEx库中还有一些其他实现,包括一个(PropertyProgress)发送进度报告INotifyPropertyChanged(通过切换回UI线程后SynchronizationContext).