2008-11-03 4 views
72

Meine WinForms App verwendet eine Anzahl von BackgroundWorker Objekte, um Informationen aus einer Datenbank abzurufen. Ich verwende BackgroundWorker, weil die Benutzeroberfläche während lang andauernder Datenbankabfragen nicht blockiert wird und das Threading-Modell für mich vereinfacht wird.Nicht behandelte Ausnahmen in BackgroundWorker

Ich bekomme gelegentlich DatabaseExceptions in einigen dieser Hintergrundthreads, und ich habe mindestens eine dieser Ausnahmen in einem Worker-Thread beim Debuggen erlebt. Ich bin ziemlich sicher, dass diese Ausnahmen Timeouts sind, von denen ich denke, dass sie von Zeit zu Zeit vernünftig sind.

Meine Frage ist, was passiert, wenn eine unbehandelte Ausnahme in einem dieser Hintergrund Worker-Threads auftritt.

Ich glaube nicht, dass ich eine Ausnahme in einem anderen Thread abfangen kann, aber kann ich erwarten, dass meine WorkerCompleted-Methode ausgeführt wird? Gibt es eine Eigenschaft oder Methode des BackgroundWorker, die ich nach Ausnahmen abfragen kann?

Antwort

77

Wenn die Operation eine Ausnahme auslöst, die Ihr Code nicht verarbeitet, die BackgroundWorker fängt die Ausnahme und leitet es in den RunWorkerCompleted Event-Handler, wo es als Fehler Eigenschaft System.ComponentModel.RunWorkerCompletedEventArgs ausgesetzt ist. Wenn Sie unter dem Visual Studio-Debugger ausgeführt werden, bricht der Debugger an dem Punkt in dem DoWork-Ereignishandler, in dem die nicht behandelte Ausnahme ausgelöst wurde, ab.

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

+5

Das wird es tun. Natürlich können Sie die Ausnahme immer in Ihrer DoWork-Methode abfangen und entsprechend reagieren (denken Sie daran, Control.Invoke zu verwenden, wenn Sie die Benutzeroberfläche aktualisieren). Aber RunWorkerCompleted ist in der Tat einfacher. –

+0

Ich finde diese Lösung. Mein RunWorkerCompleted wird nicht ausgeführt, wenn ich einfach New Exception(), aber Unhandled Exception ausgelöst habe. Fangen in DoWork ist nicht die richtige Antwort. In meinem BackgroundWorker sollte etwas nicht stimmen. – CallMeLaNN

+0

Ich habe gerade die letzten sieben Stunden damit verbracht, herauszufinden, warum meine App nicht läuft und gerade beendet wird, und alles, weil in BackgroundWorker ein Fehler aufgetreten ist, der nicht richtig erkannt wurde. +1 an dich und ich denke du verdienst auch ein Bier :-) – EvilDr

10

Standardmäßig wird es durch den Background gefangen und gespeichert werden. Von MSDN:

Wenn die Operation eine Ausnahme auslöst, die Ihr Code nicht verarbeitet, fängt die Background die Ausnahme und leitet es in den RunWorkerCompleted Event-Handler, wo es als Eigenschaft Fehler von System.ComponentModel.RunWorkerCompletedEventArgs ausgesetzt ist . Wenn Sie unter dem Visual Studio-Debugger ausgeführt werden, bricht der Debugger an dem Punkt in dem DoWork-Ereignishandler, in dem die nicht behandelte Ausnahme ausgelöst wurde, ab.

34

Ich bin voll mit BackgroundWorker über einen Zeitraum von Jahren und wirklich wissen es tief.

Erst kürzlich Mein RunWorkerCompleted verfängt nicht die e.Error, wenn ich einfach Throw New Exception("Test") in DoWork. Unhandled Exception ausgelöst. Catch in DoWork ist nicht die beste Praxis also e.Error hat keine Bedeutung.

Wenn ich versuche, neue Form mit neuen BackgroundWorker, e.Error in RunWorkerCompleted erfolgreich behandelt zu erstellen. Es sollte etwas in meinem komplizierten BackgroundWorker falsch sein.

Nach ein paar Tagen googeln und debuggen, einen Fehler versuchen. Das fand ich in meinem RunWorkerCompleted:

  • prüfen e.Error zuerst, dann e.Cancelled und schließlich e.Result
  • Sie nicht die e.Result wenn e.Cancelled = True bekommen.
  • Lassen Sie sich nicht die e.Result wenn e.Error nicht null (oder Nothing) ist **

** Dies ist, wo ich vermisse. Wenn Sie versuchen, e.Result zu verwenden, wenn e.Error nicht null (oder Nothing) ist, wird Unhandled Exception ausgelöst.


UPDATE: Im e.Result bekommen Eigenschaft .NET-Design für e.Error zuerst zu überprüfen, ob erhielt Fehler, dann werden sie die gleiche Ausnahme von DoWork erneut werfen. Deshalb erhalten wir Unhandled Ausnahme in RunWorkerCompleted, aber eigentlich ist die Ausnahme von DoWork.

Hier ist die beste Praxis in RunWorkerCompleted zu tun:

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 

Wenn Sie ein Objekt, das für alle zugänglich DoWork, Progress und RunWorkerCompleted wollen, wie folgt verwenden:

Dim ThreadInfos as Dictionary(Of BackgroundWorker, YourObjectOrStruct) 

Sie können ganz einfach Zugriff ThreadInfos(sender).Field wo immer Sie wollen.

+1

Nr. Ich legte werfen neue Ausnahme ("Test") in DoWork. Dann ausgelöst RunWorkerCompleted und ich e.Result vor e.Error aufrufen. Das ist falsch. Da e.Error nicht null ist (oder Nothing), können wir e.Result nicht aufrufen. Aufruf von e.Result wenn e.Error IsNot Nothing ODER e.Cancelled = True wird eine Unhandled Exception in RunWorkerCompleted werfen. Also checken Sie vor dem Aufruf von e.Result nach e.Error und e.Cancelled. – CallMeLaNN

+0

Der Versuch, in meinem abgeschlossenen Eventhandler auf E.Result zuzugreifen, führte zu einer TargetInvocationException, wenn mein DoWork fehlerhaft war. Ich überprüfe jetzt, ob der Fehler null ist oder ob er vor dem Zugriff auf das Ergebnis abgebrochen wurde, und mein Code ist behoben! Ich sage dir das, weil deine Erklärung mir geholfen hat, meinen Produktionscode zu korrigieren. Vielen Dank! – jlafay

+0

@CallMeLaNN Ausgezeichnete Info. Machte den gleichen Fehler. –

4

Wie es bereits bemerkt:

Wenn die Operation eine Ausnahme auslöst , dass der Code nicht verarbeitet, die Background fängt die Ausnahme und leitet es in die RunWorkerCompleted Ereignishandler, es in dem wird als Fehler Eigenschaft von System.ComponentModel.RunWorkerCompletedEventArgs ausgesetzt.

Dies ist wichtig, wenn Sie mit dem ursprünglichen Thread interagieren. Wenn das Ergebnis Ihrer Ausnahme beispielsweise in eine Art Beschriftung in Ihrem Formular geschrieben werden soll, müssen Sie die Ausnahme nicht im DoWork des BackgroundWorker abfangen, sondern stattdessen den e.Error von RunWorkerCompletedEventArgs behandeln.

Wenn Sie den Code mit Background Reflektor analysieren können Sie alle ziemlich einfach sehen seine behandelt: Ihre DoWork wird in einem Try-catch-Block ausgeführt, und die Ausnahme wird nur RunWorkerCompleted weitergegeben. Aus diesem Grund stimme ich der "bevorzugten" Methode nicht zu, alle Exceptions im DoWork-Event immer zu erfassen.

Kurz gesagt, die ursprüngliche Frage zu beantworten:

Ja - auf dem RunWorkerCompleted immer abgefeuert werden zählen. Verwenden Sie e.Error von RunWorkerCompleted, um nach Ausnahmen im anderen Thread zu suchen

3

Dies funktioniert nur ohne den angehängten Debugger. Wenn der Debugger in Visual Studio ausgeführt wird, fängt er die nicht behandelte Ausnahme in der DoWork-Methode ab und bricht die Ausführung ab. Sie können jedoch auf "Fortfahren" klicken und RunWorkerCompleted wird erreicht kann über das Feld e.Error eine Ausnahme lesen.