web-dev-qa-db-fra.com

Exceptions non gérées dans BackgroundWorker

Mon application WinForms utilise un certain nombre d'objets BackgroundWorker pour récupérer des informations dans une base de données. J'utilise BackgroundWorker car cela permet à l'interface utilisateur de rester débloquée pendant les requêtes de base de données de longue durée et cela simplifie le modèle de threading pour moi.

Je reçois occasionnellement DatabaseExceptions dans certains de ces threads d'arrière-plan, et j'ai été témoin d'au moins une de ces exceptions dans un thread de travail lors du débogage. Je suis assez confiant que ces exceptions sont des délais d'attente que je suppose qu'il est raisonnable de s'attendre de temps en temps.

Ma question concerne ce qui se passe lorsqu'une exception non gérée se produit dans l'un de ces threads de travail en arrière-plan.

Je ne pense pas que je puisse intercepter une exception dans un autre thread, mais puis-je m'attendre à ce que ma méthode WorkerCompleted soit exécutée? Y a-t-il une propriété ou une méthode de BackgroundWorker que je peux interroger pour des exceptions?

71
Ed Guiness

Si l'opération déclenche une exception que votre code ne gère pas, BackgroundWorker intercepte l'exception et la transmet au gestionnaire d'événements RunWorkerCompleted, où elle est exposée en tant que propriété Error de System.ComponentModel.RunWorkerCompletedEventArgs. Si vous exécutez sous le débogueur Visual Studio, le débogueur s'arrête au point dans le gestionnaire d'événements DoWork où l'exception non gérée a été levée.

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

77
Ed Guiness

J'utilise pleinement BackgroundWorker depuis plus d'un an et je le sais vraiment en profondeur.

Tout récemment, Mon RunWorkerCompleted n'attrape pas le e.Error Quand je simplement Throw New Exception("Test") dans DoWork. Cependant, l'exception non gérée a été soulevée. La capture dans DoWork n'est pas la meilleure pratique, donc e.Error N'a aucun sens.

Lorsque j'essaie de créer un nouveau Form avec un nouveau BackgroundWorker, e.Error Dans RunWorkerCompleted a été géré avec succès. Il devrait y avoir quelque chose de mal dans mon BackgroundWorker compliqué.

Après quelques jours de recherche et de débogage sur Google, essayez une erreur. J'ai trouvé ceci dans mon RunWorkerCompleted:

  • Vérifiez d'abord e.Error, Puis e.Cancelled Et enfin e.Result
  • N'obtenez pas le e.Result Si e.Cancelled = True.
  • N'obtenez pas le e.Result Si e.Error N'est pas null (ou Nothing) **

** C'est là que je m'ennuie. Si vous essayez d'utiliser e.Result Si e.Error N'est pas null (ou Nothing), une exception non gérée sera levée.


PDATE: Dans la propriété e.Result Get .NET, créez-le pour vérifier d'abord e.Error, Si vous obtenez une erreur, alors ils relanceront la même exception de DoWork. C'est pourquoi nous obtenons une exception non gérée dans RunWorkerCompleted mais en fait l'exception provient de DoWork.

Voici la meilleure pratique à faire dans 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

Si vous voulez un objet accessible à tous DoWork, ProgressChanged et RunWorkerCompleted, utilisez comme ceci:

Dim ThreadInfos as Dictionary(Of BackgroundWorker, YourObjectOrStruct)

Vous pouvez facilement accéder à ThreadInfos(sender).Field où vous le souhaitez.

34
CallMeLaNN

Par défaut, il sera capturé et stocké par BackgroundWorker. Depuis MSDN:

Si l'opération déclenche une exception que votre code ne gère pas, BackgroundWorker intercepte l'exception et la transmet au gestionnaire d'événements RunWorkerCompleted, où elle est exposée en tant que propriété Error de System.ComponentModel.RunWorkerCompletedEventArgs. Si vous exécutez sous le débogueur Visual Studio, le débogueur s'arrête au point dans le gestionnaire d'événements DoWork où l'exception non gérée a été levée.

10
Stu Mackellar

Comme il a déjà été noté:

Si l'opération déclenche une exception que votre code ne gère pas, BackgroundWorker intercepte l'exception et la transmet au gestionnaire d'événements RunWorkerCompleted, où elle est exposée en tant que propriété Error de System.ComponentModel.RunWorkerCompletedEventArgs.

Ceci est important lorsque vous interagissez avec le fil d'origine. Par exemple, si vous souhaitez que le résultat de votre exception soit écrit dans une sorte d'étiquette sur votre formulaire, c'est à ce moment-là que vous ne devez pas intercepter l'exception dans le DoWork du BackgroundWorker, mais gérer à la place l'e.Error de RunWorkerCompletedEventArgs.

Si vous analysez le code BackgroundWorker avec un réflecteur, vous pouvez voir que tout est assez simple: votre DoWork est exécuté dans un bloc try-catch, et l'exception est simplement passée à RunWorkerCompleted. C'est la raison pour laquelle je suis en désaccord avec la méthode "préférée" de toujours intercepter toutes vos exceptions dans l'événement DoWork.

En bref, pour répondre à la question d'origine:

Oui - vous pouvez compter sur votre RunWorkerCompleted pour toujours être renvoyé.

Utilisez e.Error de RunWorkerCompleted pour vérifier les exceptions dans l'autre thread.

4
Vedran

Cela ne fonctionnera que sans le débogueur attaché, lorsqu'il est exécuté à partir de Visual Studio, le débogueur interceptera l'exception non gérée dans la méthode DoWork et interrompra l'exécution, mais vous pouvez cliquer sur continuer et RunWorkerCompleted sera atteint et vous pourrez lire l'exception via le champ e.Error.

3
TzOk