【问题标题】:How are async void event handlers called in C#?如何在 C# 中调用异步 void 事件处理程序?
【发布时间】:2014-12-01 12:30:46
【问题描述】:

如果我将我的事件处理程序声明为async void,它们会被 .NET 框架同步调用还是异步调用?

即,给定以下代码:

async void myButton_click(object sender, EventArgs e) {
   do stuff
   await longRunning()
   do more stuff
}

我可以确定“do stuff”行将在 GUI 线程上执行吗?

【问题讨论】:

  • 请对不赞成票发表评论。谢谢。

标签: c# .net event-handling async-await


【解决方案1】:

事件处理程序将被同步调用,并且不会等待(等待)直到事件处理程序完成,而是等到事件处理程序返回。

如果前面的句子足够混乱,我会尽量解释清楚。当所有的等待点都执行完毕并且到达方法体的末尾或执行任何返回语句时,异步方法完成(忽略异常)。但是,只要您点击尚未完成的 Task 的第一个等待语句,异步方法就会返回。换句话说,异步方法可以返回多次,但只能完成一次。

所以现在我们知道异步方法何时完成并返回。事件处理程序将假定您的方法在返回时已完成,而不是在实际完成时。

一旦您的事件处理程序到达第一个await 语句,它将返回,如果有更多方法附加到同一个事件处理程序,它将继续执行它们而不等待异步方法完成。

是的,如果 UI 线程触发事件,do stuff 将在 UI 线程中执行,是的,do more stuff 也将在 UI 线程中执行,只要不调用 longRunning().ConfigureAwait(false)

【讨论】:

  • 谢谢,你的第一句话对我来说已经足够了:)
  • @Eyvind 并非所有人都很聪明。这就是为什么我用另一个词解释,假设它会帮助未来的读者:)
  • 我认为你最后一段中的措辞有点误导;即使在longRunning 中调用了ConfigureAwait(false)do more stuff 也会在 UI 线程中执行。
  • @StephenCleary 删除了该部分。我想我在这里遗漏了一些东西,需要在这部分工作。谢谢。
  • @SriramSakthivel:是的。我是这样想的:每个方法都是独立的;它在await 捕获自己的上下文并在该上下文中恢复。因此,如果调用堆栈下方的方法没有在其上下文中恢复,那没关系。
【解决方案2】:

它们将像调用任何其他非async-await 方法一样被调用:

Click(this, EventArgs.Empty);

因为这个特定的事件处理程序是一个async 方法,所以调用将同步运行,直到达到await,其余的将是一个延续。 这意味着do stuff 在GUI 线程上同步执行。然后调用者在不知道async 操作尚未完成的情况下继续前进。

do more stuff 也将在 GUI 线程上执行,但出于不同的原因。 GUI 环境中的 SynchronizationContext 确保将继续发布到单个 GUI 线程,除非您明确告诉它不要使用 await longRunning().ConfigureAwait(false)

【讨论】:

    猜你喜欢
    • 2013-01-06
    • 1970-01-01
    • 2016-04-11
    • 1970-01-01
    • 1970-01-01
    • 2013-04-02
    • 2012-09-02
    • 2018-02-13
    • 1970-01-01
    相关资源
    最近更新 更多