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?
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
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
:
e.Error
, Puis e.Cancelled
Et enfin e.Result
e.Result
Si e.Cancelled = True
.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.
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.
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.
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.