在 C# 5 中将 WPF 事件处理程序声明为“async”的意义
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12556993/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Significance of declaring a WPF event handler as 'async' in C# 5
提问by Drew Noakes
Imagine a WPF code-behind event handler:
想象一个 WPF 代码隐藏事件处理程序:
<Button Click="OnButtonClick" />
In C# 4 you would declare your handler as:
在 C# 4 中,您可以将处理程序声明为:
private void OnButtonClick(object sender, RoutedEventArgs e) { ... }
In C# 5 you can declare an asynchandler
在 C# 5 中,您可以声明一个async处理程序
private async void OnButtonClick(object sender, RoutedEventArgs e) { ... }
So what is WPF doing with this? A few minutes of searching about didn't turn anything up.
那么 WPF 用这个做什么呢?几分钟的搜索没有发现任何东西。
It seems that it's possible to perform UI updates after awaitstatements. Does this imply that the task is continued on the Dispatcher thread?
似乎可以在await语句之后执行 UI 更新。这是否意味着任务在 Dispatcher 线程上继续?
If the Taskraised an error, would it be raised through the WPF Dispatcher, or only via the TaskScheduler?
如果Task引发错误,它是通过 WPF 引发的Dispatcher,还是仅通过TaskScheduler?
Are there any other interesting aspects to this that might be nice to understand?
是否还有其他有趣的方面可能很容易理解?
回答by Stephen Cleary
You may find my async/await introhelpful.
你可能会发现我的async/await 介绍很有帮助。
An asyncmethod is re-written by the compiler to support the awaitoperator. Every asyncmethod starts out synchronous (in this case, on the UI thread) until it awaits some operation (that is not already completed).
async编译器重写了一个方法来支持await操作符。每个async方法都是同步开始的(在这种情况下,在 UI 线程上),直到它await的某个操作(尚未完成)。
By default, the context is saved, and when the operation completes, the rest of the method is scheduled to execute in that context. The "context" here is SynchronizationContext.Currentunless it is null, in which case it is TaskScheduler.Current. As Drew pointed out, WPF provides a DispatcherSynchronizationContextwhich is tied to the WPF Dispatcher.
默认情况下,上下文被保存,当操作完成时,方法的其余部分被安排在该上下文中执行。这里的“上下文”是SynchronizationContext.Current除非它是null,在这种情况下它是TaskScheduler.Current。正如 Drew 所指出的,WPF 提供了一个DispatcherSynchronizationContext与 WPF 相关的Dispatcher。
Regarding error handling:
关于错误处理:
When you awaita Taskinside a WPF async voidevent handler, the error handling goes like this:
当你await一个Task一个WPF中async void的事件处理程序,错误处理是这样的:
- The
Taskcompletes with an error. The exception is wrapped into anAggregateException, like allTaskerrors. - The
awaitoperator sees that theTaskcompleted with an error. It unwraps the original exception and re-throws it, preserving the original stack trace. - The
async voidmethod builder catches the exception escaping from anasync voidmethod and passes it to theSynchronizationContextthat was active when theasync voidmethod started executing (in this case, the same WPF context). - The exception is raised (with the original stack trace, and without any annoying
AggregateExceptionwrapping) on theDispatcher.
- 该
Task出错完成。异常被包装成一个AggregateException,就像所有Task错误一样。 - 该
await操作员发现的Task完成了一个错误。它解开原始异常并重新抛出它,保留原始堆栈跟踪。 - 该
async void方法构建器捕捉异常从逸出async void的方法和把它传递给SynchronizationContext处于活动状态时,async void方法开始执行(在这种情况下,相同的WPF上下文)。 - 将引发异常(与原来的堆栈跟踪,并没有任何恼人的
AggregateException包装)上Dispatcher。
This is rather convoluted, but the intent is to have exceptions raised from asyncevent handlers be practically the same as exceptions raised from regular event handlers.
这是相当复杂的,但其目的是使从async事件处理程序引发的异常实际上与从常规事件处理程序引发的异常相同。
回答by Drew Noakes
A partial answer. From MSDN:
部分答案。从MSDN:
An async method that has a void return type can't be awaited, and the caller of a void-returning method can't catch any exceptions that the method throws.
无法等待具有 void 返回类型的异步方法,并且返回 void 的方法的调用者无法捕获该方法抛出的任何异常。
So any errors would only be available via the TaskScheduler.
因此,任何错误都只能通过TaskScheduler.
Also, there's nothing XAML-specific going on with the event handler registration. It could have been done in code:
此外,事件处理程序注册没有任何特定于 XAML 的内容。它可以在代码中完成:
this.button.Click += OnButtonClick;
Or even as an async lambda:
甚至作为异步 lambda:
this.button.Click += async (s,e) => { ... };
As for safety of UI updates after an await, it seems that the continuation is executed within SynchronisationContext.Current, which is set per thread. In WPF, this is a DispatcherSynchronisationContextthat's coupled to the WPF Dispatcherthat pumped the event in the first place.
至于 之后 UI 更新的安全性await,似乎在 内执行延续SynchronisationContext.Current,这是每个线程设置的。在 WPF 中,这是与首先抽取事件DispatcherSynchronisationContext的 WPF 耦合的Dispatcher。

