BackoundWorker 中的未处理异常

我的 WinForms 应用程序使用许多 背景工作者对象从数据库中检索信息。我使用 BackoundWorker 是因为它允许在长时间运行的数据库查询期间 UI 保持畅通,并且它简化了我的线程模型。

在这些后台线程中,我偶尔会遇到 DatabaseException 异常,在调试时,我至少在一个工作线程中看到过其中一个异常。我相当肯定这些例外是超时,我认为这是合理的期望不时。

我的问题是,当这些后台工作线程之一发生未处理的异常时,会发生什么情况。

我不认为我可以在另一个线程中捕获异常,但是我可以期望我的 WorkerCompleted 方法被执行吗?是否有任何属性或方法的后台工作者,我可以询问异常?

26027 次浏览

如果操作引发了代码不处理的异常,则 BackgroundWorker捕获该异常并将其传递给 RunWorkerCompleted事件处理程序,在该处理程序中,该异常作为 System.ComponentModel.RunWorkerCompletedEventArgs的 Error 属性公开。如果在 VisualStudio 调试器下运行,则调试器将在 DoWork 事件处理程序中引发未处理异常的点中断。

Http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.dowork.aspx

默认情况下,它将被 Background Worker 捕获和存储:

如果操作引发了代码不能处理的异常,Background Worker 将捕获该异常并将其传递给 RunWorkerCompleted 事件处理程序,在该处将该异常公开为 System 的 Error 属性。组件模型。RunWorkerCompletedEventArgs.如果在 VisualStudio 调试器下运行,则调试器将在 DoWork 事件处理程序中引发未处理异常的点中断。

我完全使用 BackgroundWorker超过一年,真的知道它在深处。

就在最近,我的 RunWorkerCompleted没有赶上的 e.Error时,我只是 Throw New Exception("Test")DoWork。但是引发了未处理的异常。在 DoWork中捕捉并不是最好的做法,因此 e.Error没有意义。

当我尝试用新的 BackgroundWorker创建新的 Form时,成功地处理了 RunWorkerCompleted中的 e.Error。我复杂的 BackgroundWorker应该有问题。

经过几天的搜索和调试,我尝试了一个错误。我在我的 RunWorkerCompleted中发现了这个:

  • 先检查 e.Error,然后是 e.Cancelled,最后是 e.Result
  • 不要得到 e.Result,如果 e.Cancelled = True
  • 如果 e.Error不是 null(或 Nothing) * * ,则不要得到 e.Result

* 这就是我想念的地方 * 。如果尝试使用 e.Result(如果 e.Error不是 null(或 Nothing)) ,则将引发未处理的异常。


更新:e.Result中获取属性。NET 设计它首先检查 e.Error,如果得到错误,然后他们将重新抛出相同的异常从 DoWork。这就是为什么我们在 RunWorkerCompleted中得到未处理的异常,但实际上异常来自于 DoWork

下面是在 RunWorkerCompleted中要做的最佳实践:

If e.Error IsNot Nothing Then
' Handle the error here
Else
If e.Cancelled Then
' Tell user the process canceled here
Else
' Tell user the process completed
' and you can use e.Result only here.
End If
End If

如果您想要一个对所有 DoWork、 ProgressChanged 和 RunWorkerCompleted 都可访问的对象,可以这样使用:

Dim ThreadInfos as Dictionary(Of BackgroundWorker, YourObjectOrStruct)

您可以轻松地访问 ThreadInfos(sender).Field任何您想要的地方。

正如已经指出的那样:

如果操作引发异常 您的代码不能处理的,则 BackoundWorker 捕获异常 然后传递到 完成的事件处理程序, 在哪里暴露为错误 财产 System.Component 模型。

当您与原始线程交互时,这一点很重要。例如,如果你想把异常的结果写在表单上的某种标签上,那么你就不能在 BackoundWorker 的 DoWork 中捕获异常,而是处理来自 RunWorkerCompletedEventArgs 的 e 错误。

如果你使用反射器分析 BackoundWorker 代码,你会发现它的处理非常简单: 您的 DoWork 将在 try-catch 块中执行,并且异常将被传递给 RunWorkerCompleted。 这就是为什么我不同意总是在 DoWork 事件中捕获所有异常的“首选”方法。

简而言之,回答最初的问题:

-你可以指望你的 RunWorkerCompleted 总是被解雇。

使用 RunWorkerCompleted 中的 错误检查另一个线程中的异常。

这只能在没有调试器的情况下工作,当从 Visual Studio 运行时,调试器将捕获 DoWork 方法中的非手动异常,并将中断执行,但是你可以点击继续,RunWorkerCompleted 将被达到,你将能够通过 e 错误字段读取异常。