20

想象一个 WPF 代码隐藏事件处理程序:

<Button Click="OnButtonClick" />

在 C# 4 中,您可以将处理程序声明为:

private void OnButtonClick(object sender, RoutedEventArgs e) { ... }

在 C# 5 中,您可以声明一个async处理程序

private async void OnButtonClick(object sender, RoutedEventArgs e) { ... }

那么 WPF 对此做了什么?几分钟的搜索并没有发现任何问题。

似乎可以在await语句之后执行 UI 更新。这是否意味着任务在 Dispatcher 线程上继续?

如果Task引发错误,将通过 WPF 引发错误,Dispatcher还是仅通过TaskScheduler?

是否还有其他有趣的方面可以很好理解?

4

2 回答 2

25

你可能会发现我的async/await 介绍很有帮助。

async编译器重写了一个方法来支持await操作符。每个async方法都开始同步(在这种情况下,在 UI 线程上),直到它await是某个操作(尚未完成)。

默认情况下,会保存上下文,当操作完成时,方法的其余部分会安排在该上下文中执行。这里的“上下文”是SynchronizationContext.Current除非它是null,在这种情况下它是TaskScheduler.Current。正如 Drew 指出的那样,WPF 提供了一个DispatcherSynchronizationContext与 WPF 相关联的Dispatcher

关于错误处理:

当您awaitTaskWPFasync void事件处理程序中时,错误处理如下所示:

  • 完成时出现Task错误。AggregateException与所有Task错误一样,异常被包装在一个中。
  • await操作员看到完成但Task有错误。它解开原始异常并重新抛出它,保留原始堆栈跟踪。
  • 方法构建器捕获从方法async void中转义的异常async void,并将其传递给方法开始执行SynchronizationContext时处于活动状态的异常async void(在本例中,相同的 WPF 上下文)。
  • AggregateExceptionDispatcher. _

这是相当复杂的,但目的是让async事件处理程序引发的异常与常规事件处理程序引发的异常几乎相同。

于 2012-09-24T01:57:18.747 回答
6

部分答案。来自MSDN

无法等待具有 void 返回类型的异步方法,并且返回 void 的方法的调用者无法捕获该方法抛出的任何异常。

所以任何错误只能通过TaskScheduler.

此外,事件处理程序注册没有任何特定于 XAML 的内容。它可以在代码中完成:

this.button.Click += OnButtonClick;

甚至作为异步 lambda:

this.button.Click += async (s,e) => { ... };

至于 之后的 UI 更新的安全性await,似乎延续是在SynchronisationContext.Current每个线程设置的内部执行的。在 WPF 中,这是一个DispatcherSynchronisationContext与 WPF 耦合的,Dispatcher它首先泵送事件。

于 2012-09-23T23:18:17.820 回答