为什么没有为异步函数中的异常触发Application_UnhandledException?

时间:2013-12-19 07:35:03

标签: vb.net silverlight async-await caliburn.micro

我有一个异步成员,通过Caliburn.Micro约定调用并抛出异常。当此成员是“Async Sub”时,异常将在Application_UnhandledException方法中处理。当我将其更改为“异步函数”(它只返回一个任务)时,Application_UnhandledException方法不再处理该异常。应用程序不会崩溃,只是似乎吞没了异常。

通过Caliburn.Micro约定绑定的按钮 - Click事件绑定到DataContext上具有相同名称的方法。

<Button x:Name="MyMember" Content="My Button" />

Async Sub Variant - 引发了Application_UnhandledException

Public Async Sub MyMember()

异步函数变体 - 吞噬异常

Public Async Function MyMember() As System.Threading.Tasks.Task

documentation for Application_UnhandledException表示将会提出

  

...对于应用程序代码未处理的每个抛出异常。

这不是真的情况,或者在处理异常的调用堆栈中还有一些应用程序代码 - 可能在Caliburn.Micro中。

this example中似乎有类似的东西 - 尽管在C#中。

任何人都可以解释这种行为吗?像这样吞下异常是一个很大的担忧。

更新 我无法应用此信息,但可能相关。 You can use Coroutine.Completed event to get your exception.

更新2: Valeriu Caraulean解决了是否在Caliburn.Micro here中使用async void或async Task的问题。他参考了斯蒂芬的文章Best Practices in Asynchronous Programming,在那里他提供了以下指导。

  

...你应该更喜欢async Task to async void。异步任务方法   使错误处理,可组合性和可测试性更容易。该   本指南的例外是异步事件处理程序,它必须是   返回无效。此异常包括逻辑事件的方法   处理程序,即使它们不是字面上的事件处理程序(例如,   ICommand.Execute实现)。

更新3: 我在Caliburn.Micro论坛上发布了related question

3 个答案:

答案 0 :(得分:4)

当您开始使用Async时,“调用堆栈”失去了很多意义。这是实际发生的事情:

  • Async Function方法返回TaskTask<T>,并且从该方法抛出的任何异常都将放在返回的任务上。
  • Async Sub方法没有Task可以放置异常,因此从该方法抛出的任何异常都会在SynchronizationContext上重新引发, Async Sub方法。

所以,这意味着Async Function例外被忽略,除非你观察它们(例如,Await任务或读取它的Exception属性。当任务被垃圾收集时,未观察到的异常会传递给TaskScheduler.UnobservedTaskException,但此事件会在请求或应用程序上下文的 之外引发(因此您无法做出响应)

另一方面,代表请求上下文的Async Sub上会重新提出SynchronizationContext个异常,这就是他们传递给Application.UnhandledException的原因。

理想情况下,Caliburn.Micro应该了解Task处理程序。如果他们不这样做,那么您需要联系该团队以获得最佳解决方案。仅使用Async Sub的问题是,当调用代码认为它已经完成时,它将继续执行;这可能会也可能不会引起问题 - 这完全取决于Caliburn.Micro。至少Async Function,您通知调用代码正在进行的操作(通过返回Task),但是是否等待该任务(以及是否观察到它的异常)又完全取决于Caliburn.Micro。

答案 1 :(得分:2)

如果在任务中抛出异常,则任务将出现故障。您可以通过获取Task.IsFaulted属性来确定这一点。如果此属性为true,则Task.Exception属性将包含AggregateException,该Application_UnhandledException组合了任务未处理的异常。

处理任务中异常的最简单方法是等待任务。未处理的异常将由异步的魔力和等待重新出现在等待任务的线程上。

如果您的任务是“即发即忘”任务,则无法处理任何未处理的异常,因为您无法检查或等待任务。在这种情况下,您应该通过捕获任务中的异常来确保您的任务没有任何未处理的异常。

您还可以处理TaskScheduler.UnobservedTaskException事件。这有点类似于{{1}}事件,除了它是用于任务。

答案 2 :(得分:-1)

任务吞下异常,除非您等待它们(例如,C#await关键字)。如果我没记错的话,可以在事后检索任务抛出的任何异常。这就是为什么警告经常在Visual Studio中发布(至少在C#中),而不是等待任务。