如果我将事件处理程序声明为 async void
,.NET 框架会同步调用还是异步调用它们?
即,给定以下代码:
async void myButton_click(object sender, EventArgs e) {
do stuff
await longRunning()
do more stuff
}
我可以确定"做事"行将在 GUI 线程上执行吗?
事件处理程序将被同步调用,并且不会等待(await(直到事件处理程序完成,而是等到事件处理程序返回。
如果前面的句子足够混乱,我会试着解释清楚。当执行所有等待点并且到达方法主体的末尾或执行任何返回语句时,异步方法完成(忽略异常(。但是异步方法会在您点击尚未完成的Task
的第一个 await 语句后立即返回。换句话说,异步方法可以返回多次,但只能完成一次。
所以现在我们知道异步方法何时完成并返回。事件处理程序将假定您的方法在返回时立即完成,而不是在实际完成时完成。
一旦你的事件处理程序到达第一个语句await
它就会返回,如果有多个方法附加到同一个事件处理程序,它将继续执行它们,而无需等待异步方法完成。
do stuff
将在 UI 线程中执行,是的,只要不调用longRunning().ConfigureAwait(false)
,do more stuff
也会在 UI 线程中执行。
它们将被调用,就像调用任何其他非async-await
方法一样:
Click(this, EventArgs.Empty);
由于此特定事件处理程序是async
方法,因此调用将同步运行,直到达到await
,其余部分将是延续。这意味着do stuff
在 GUI 线程上同步执行。然后,调用方在不知道async
操作尚未完成的情况下继续前进。
do more stuff
也将在 GUI 线程上执行,但原因不同。GUI 环境中的SynchronizationContext
确保将延续发布到单个 GUI 线程,除非您明确告诉它不要使用await longRunning().ConfigureAwait(false)