BackgroundWorker中未处理的异常


71

我的WinForms应用程序使用许多BackgroundWorker对象从数据库检索信息。我之所以使用BackgroundWorker,是因为它允许UI在长时间运行的数据库查询期间保持畅通无阻,并且为我简化了线程模型。

我在其中某些后台线程中偶尔遇到DatabaseException,并且在调试时目睹了在工作线程中至少有这些异常之一。我非常相信这些异常是超时,我认为它是不时会发生的合理事件。

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

我认为我无法在另一个线程中捕获异常,但是我可以期望执行我的WorkerCompleted方法吗?是否可以查询BackgroundWorker的任何属性或方法来处理异常?

Answers:


77

如果该操作引发了代码无法处理BackgroundWorker的异常,则捕获该异常并将其传递到RunWorkerCompleted事件处理程序中,在此将其显示为的Error属性System.ComponentModel.RunWorkerCompletedEventArgs。如果您在Visual Studio调试器下运行,则调试器将在DoWork事件处理程序中引发未处理异常的点处中断。

http://msdn.microsoft.com/zh-CN/library/system.componentmodel.backgroundworker.dowork.aspx


5
会的。当然,您总是可以在DoWork方法中捕获异常,并做出相应的反应(如果要更新UI,请记住使用Control.Invoke)。但是RunWorkerCompleted确实更简单。
马克·格雷夫

我正在寻找这种解决方案。当我简单地抛出New Exception()却引发了Unhandled Exception时,我的RunWorkerCompleted无法运行。赶上DoWork并不是真正的答案。我的BackgroundWorker中应该有问题。
2010年

我刚刚花了七个小时的时间来弄清楚为什么我的应用程序无法运行并仅退出的原因,所有这些都是因为BackgroundWorker中发生了一个错误,无法正确捕获。+1给您,我想您也应该喝啤酒:-)
EvilDr

1
@EvilDr谢谢,我喜欢真正的啤酒。
Ed Guiness 2013年

假设您在贝德福德(Bedford),如果我到达那儿,我将很荣幸。或者,如果工作将您带到北约克郡,我也会在这里荣幸:-)
EvilDr

34

我已经使用BackgroundWorker了多年,并且对它非常了解。

就在最近,当我只是进入时,我RunWorkerCompleted还没有抓住。但是引发了未处理的异常。赶上并不是最佳实践,因此毫无意义。e.ErrorThrow New Exception("Test")DoWorkDoWorke.Error

当我尝试创建新FormBackgroundWorkere.ErrorRunWorkerCompleted成功地处理。我复杂的东西应该出毛病了BackgroundWorker

经过几天的谷歌搜索和调试,尝试执行错误。我在我的RunWorkerCompleted

  • e.Error首先检查,然后e.Cancelled最后检查e.Result
  • 不要得到e.Resultif e.Cancelled = True
  • 不要得到e.Resultif e.Errornot null(或Nothing)**

**这是我想念的地方。如果尝试使用e.Resultif e.Errornot null(或Nothing),则将引发Unhandled Exception。


更新:e.Result.NET设计的get属性中,e.Error首先进行检查,如果发现错误,则它们将重新抛出与相同的异常DoWork。这就是为什么我们收到Unhandled异常,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任何地方。


1
否。我在DoWork中放置了Throw New Exception('Test')。然后触发RunWorkerCompleted,并在e.Error之前调用e.Result。错了 由于e.Error不为null(或Nothing),因此无法调用e.Result。当e.Error不为空或e.Cancelled = True时调用e.Result将在RunWorkerCompleted中引发未处理的异常。因此,在调用e.Result之前检查e.Error和e.Cancelled。
CallMeLaNN

当我的DoWork错误出现时,尝试访问已完成的事件处理程序中的E.Result时将引发TargetInvocationException。现在,我在访问结果之前检查错误是否为null或是否已取消,并且我的代码已修复!我之所以告诉您,是因为您的解释帮助我修复了我的生产代码。非常感谢!
杰夫·拉法

@CallMeLaNN出色的信息。在做同样的错误。
苏厄姆·达斯古普塔

1
@SohamDasgupta自从引入TPL之后,我再也不会使用BackgroundWorker或创建自己的线程了。通过玩Task对象来执行内联代码真的很棒。它使我的代码更干净,并让.NET管理线程。使线程安全报告进度或完成进度有点困难,但是一旦获得进度,它真的很不错……
CallMeLaNN 2012年

哇,这对我有很大帮助。完全一样的事情发生在我身上。谢谢。
derFunk 2014年

10

默认情况下,它将被BackgroundWorker捕获并存储。从MSDN:

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


4

如前所述:

如果该操作引发了代码无法处理的异常,则BackgroundWorker将捕获该异常并将其传递到RunWorkerCompleted事件处理程序,在该事件处理程序中将其公开为System.ComponentModel.RunWorkerCompletedEventArgs的Error属性。

每当您与原始线程进行交互时,这一点都很重要。例如,如果您希望将异常的结果写在表单上的某种标签上,那么您就不必在BackgroundWorker的DoWork中捕获异常,而应处理RunWorkerCompletedEventArgs中的e.Error。

如果您使用反射器分析BackgroundWorker代码,您将看到其处理起来非常简单:DoWork在try-catch块中执行,并且该异常仅传递给RunWorkerCompleted。这就是为什么我不同意总是在DoWork事件中捕获所有异常的“首选”方法的原因。

简而言之,要回答原始问题:

是的-您可以依靠RunWorkerCompleted使其始终被解雇。

使用RunWorkerCompleted中的e.Error检查另一个线程中的异常。


3

从Visual Studio运行时,调试器将在DoWork方法中捕获未处理的异常并中断执行,这仅在不连接调试器的情况下起作用,但是您可以单击“继续”,并且将达到RunWorkerCompleted,并且您将能够读取异常通过e.Error字段。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.