async void,await和exceptions - 为什么从GUI线程'await'后抛出的异常需要AsyncVoidMethodBuilder进行封送处理?

时间:2014-01-22 20:48:42

标签: exception asynchronous c#-5.0 async-await

在C#5.0 In A Nutshell,第590页,给出了以下示例:

async void ButtonClick (object sender, RoutedEventArgs args) 
{ 
   await Task.Delay(1000); 
   throw new Exception ("Will this be ignored?"); 
}

本书指出不会捕获异常,因为该函数将立即返回到await行的消息循环,并且当一秒钟之后抛出异常时,它将不会被消息中的try / catch捕获循环。

本书继续说明AsyncVoidMethodBuilder必须将延续封装在另一个函数中,以便它可以构建另一个try / catch块,并将任何捕获的异常转发到同步上下文(如果存在)。

这让我感到困惑,因为我认为,因为从GUI线程调用Task.Delay,同步化上下文的存在将导致Task.Delay的继续在GUI线程上执行。我原以为它可以因此直接从try / catch子句中的消息循环继续执行,并且仍然被捕获而无需封装在另一个函数中。

我错过了什么?

1 个答案:

答案 0 :(得分:4)

你的理解是正确的;该异常将直接在UI SynchronizationContext上重新引发,并被消息循环捕获。从那里它将传递给UI应用程序范围的处理程序。

本书实际上说的是在async方法返回后无法捕获异常,这就是AsyncVoidMethodBuilder将在适当的SynchronizationContext上重新引发异常的原因。

这很重要,因为async void方法可能会“留下”其UI上下文,例如,使用ConfigureAwait(false)。但是如果在该点之后发生异常,它必须重新同步到原始SynchronizationContext,而不一定是抛出异常时的上下文。