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

发布于 2024-12-31 23:32:24 字数 1264 浏览 5 评论 0 原文

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

有两种方法(据我所知,也许更多)可以实现此目的:

  1. 通过使用目标线程同步上下文中的 TaskScheduler 来调度任务,以及
  2. 使用 Dispatcher 调用委托/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);

我喜欢基于 TPL 的版本,因为我已经经常使用 TPL,它为我提供了很大的灵活性,例如等待任务或将其与其他任务链接。然而,到目前为止,在我见过的大多数 WPF 代码示例中,都使用了 Dispatcher 变体。

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

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

不即不离 2025-01-07 23:32:24

我强烈建议您阅读基于任务的异步模式文档。这将允许您构建 API,以便在 asyncawait 出现时做好准备。

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

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

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

然后相对简单地提供一个基本实现:(

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本质上是TaskScheduler.FromCurrentSynchronizationContext,不需要实际的任务)。

异步 CTP 包含 IProgressProgress 类型,该类型类似于上面的 EventProgress(但性能更高) )。如果你不想安装CTP级别的东西,那么你可以只使用上面的类型。

总而言之,实际上有四种选择:

  1. IProgress - 这是将来编写异步代码的方式。它还迫使您将后台操作逻辑UI/ViewModel更新代码分开,这是一件好事。
  2. TaskScheduler - 不错的方法;在切换到 IProgress 之前,我使用了很长一段时间。不过,它不会强制 UI/ViewModel 更新代码脱离后台操作逻辑。
  3. SynchronizationContext - 与 TaskScheduler 相同的优点和缺点,来自 一个鲜为人知的 API
  4. Dispatcher - 真的不能推荐这个!考虑更新 ViewModel 的后台操作 - 因此进度更新代码中没有任何特定于 UI 的内容。在这种情况下,使用 Dispatcher 只是将您的 ViewModel 绑定到您的 UI 平台。可恶的。

PS 如果您确实选择使用异步 CTP,那么我的 Nito 中还有一些额外的 IProgress 实现。 AsyncEx 库,包括一个 (PropertyProgress),它通过 INotifyPropertyChanged 发送进度报告(通过切换回 UI 线程后) SynchronizationContext)。

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).

岁月如刀 2025-01-07 23:32:24

我通常使用 Dispatcher 来执行任何小型 UI 操作,并且仅在涉及大量繁重处理时才使用任务。我还将 TPL 用于与 UI 无关的所有后台任务。

您可以使用Dispatcher在不同的调度程序优先级,在使用 UI 时通常很有用,但是我发现如果后台任务涉及大量繁重处理,它仍然会锁定 UI 线程,这就是我不这样做的原因't将其用于大型任务。

I usually use the Dispatcher for any small UI operations, and only use Tasks if a lot of heavy processing is involved. I also use TPL for all background tasks that are not UI-related.

You can use the Dispatcher to run tasks at different Dispatcher Priorities, which is often useful when working with the UI, however I find it still locks up the UI thread if there is a lot of heavy processing involved in the background task, which is why I don't use it for large tasks.

涫野音 2025-01-07 23:32:24

我对所有繁重的操作使用基于任务的操作,但如果我需要更新 GUI,我会使用调度程序,否则您将遇到跨线程异常。据我所知,您必须使用调度程序来更新 GUI

更新:pete Brown 在他的博客文章 http://10rem.net/blog/2010/04/23/essential-silverlight-and-wpf-skills-the-ui-thread-dispatchers-background-workers-and-async-network-编程

I use task based for all heavy operations, but if I need to update the GUI I use the dispatcher otherwise you'll get cross threading exceptions. As far as I'm aware you have to use the dispatcher to update the GUI

update: pete brown covers it well in his blog post http://10rem.net/blog/2010/04/23/essential-silverlight-and-wpf-skills-the-ui-thread-dispatchers-background-workers-and-async-network-programming

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文