Exceptions non gérées dans BackgroundWorker
-
06-07-2019 - |
Question
Mon application WinForms utilise plusieurs BackgroundWorker . objets pour récupérer des informations d'une base de données. J'utilise BackgroundWorker parce que cela permet à l'interface utilisateur de rester débloquée lors de requêtes de base de données longues et simplifie le modèle de threading pour moi.
Je reçois occasionnellement des exceptions DatabaseException 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 auxquels 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? Existe-t-il une propriété ou une méthode de BackgroundWorker que je peux interroger à la recherche d’exceptions?
La solution
Si l'opération déclenche une exception que votre code ne gère pas, le BackgroundWorker
capture l'exception et la transmet au gestionnaire d'événements RunWorkerCompleted
, où elle est exposée en tant que Propriété d'erreur de System.ComponentModel.RunWorkerCompletedEventArgs
. Si vous exécutez le débogueur Visual Studio, celui-ci sera interrompu au point du gestionnaire d'événements DoWork où l'exception non gérée a été déclenchée.
http://msdn.microsoft.com/ en-us / library / system.componentmodel.backgroundworker.dowork.aspx
Autres conseils
J'utilise pleinement BackgroundWorker
depuis plusieurs années et je le connais vraiment en profondeur.
Récemment, mon RunWorkerCompleted
n'intercepte pas le e.Error
lorsque je lance simplement une nouvelle exception (" Test &);
dans < code> DoWork . Cependant, exception non gérée levée. La capture dans DoWork
n'est pas la meilleure pratique. Par conséquent, e.Error
n'a pas de sens.
Lorsque j'essaie de créer un nouveau Form
avec le nouveau BackgroundWorker
, e.Error
dans RunWorkerCompleted
a été géré avec succès. Il devrait y avoir quelque chose qui cloche dans mon BackgroundWorker
compliqué.
Après quelques jours à googler et à déboguer, essayez une erreur. J'ai trouvé cela dans mon RunWorkerCompleted
:
- Vérifiez d'abord
e.Error
, puise.Cancelled
et enfine.Result
- Ne pas obtenir le
e.Result
sie.Cancelled = True
. - Ne pas obtenir le
e.Result
sie.Error
n'est pasnull
(ourien
) * *
** C'est ici que je manque. Si vous essayez d'utiliser e.Result
si e.Error
n'est pas null
(ou rien
), exception gérée sera jeté.
MISE À JOUR:
Dans la propriété e.Result
get .NET, concevez-le pour vérifier e.Error
en premier. En cas d'erreur, ils renverront la même exception à partir de DoWork
. C’est la raison pour laquelle nous obtenons une exception non gérée dans RunWorkerCompleted
, mais en réalité l’exception provient de DoWork
.
Voici la meilleure pratique à suivre 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 souhaitez un objet accessible à tous DoWork, ProgressChanged et RunWorkerCompleted, utilisez la méthode suivante:
Dim ThreadInfos as Dictionary(Of BackgroundWorker, YourObjectOrStruct)
Vous pouvez facilement accéder à ThreadInfos (expéditeur) .Field
où vous voulez.
Par défaut, il sera capturé et stocké par BackgroundWorker. À partir de MSDN:
Si l'opération génère une exception que votre code ne gère pas, BackgroundWorker capture 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 le débogueur Visual Studio, celui-ci sera interrompu au point du gestionnaire d'événements DoWork où l'exception non gérée a été déclenchée.
Comme cela a déjà été noté:
Si l'opération déclenche une exception que votre code ne gère pas, le BackgroundWorker intercepte l'exception et le passe dans le Gestionnaire d'événements RunWorkerCompleted, où il est exposé comme erreur propriété de System.ComponentModel.RunWorkerCompletedEventArgs.
Ceci est important lorsque vous interagissez avec le fil de discussion d'origine. Par exemple, si vous souhaitez que le résultat de votre exception soit écrit dans une sorte d'étiquette sur votre formulaire, vous ne devez pas intercepter l'exception dans DoWork de BackgroundWorker, mais gérer e.Error à partir de RunWorkerCompletedEventArgs.
Si vous analysez le code BackgroundWorker avec un réflecteur, vous verrez qu'il est très simple à gérer: Votre DoWork est exécuté dans un bloc try-catch et l'exception est simplement transmise à RunWorkerCompleted. C’est la raison pour laquelle je ne suis pas d’accord avec la méthode «préférée» consistant à toujours intercepter toutes vos exceptions dans l’événement DoWork.
En bref, pour répondre à la question initiale:
Oui , vous pouvez compter sur votre RunWorkerCompleted pour toujours être renvoyé.
Utilisez e.Error dans RunWorkerCompleted pour rechercher des exceptions dans l'autre thread.
Ceci fonctionnera uniquement sans le débogueur attaché. Lorsqu'il sera exécuté à partir de Visual Studio, il détectera l'exception non gérée dans la méthode DoWork et interrompra l'exécution. Toutefois, vous pourrez cliquer sur Continuer et RunWorkerCompleted sera atteint et vous pourrez lire exception via le champ e.Error.