Je souhaite déclencher une tâche à exécuter sur un thread d'arrière-plan. Je ne veux pas attendre la fin des tâches.
Dans .net 3.5, j'aurais fait ceci:
ThreadPool.QueueUserWorkItem(d => { DoSomething(); });
Dans .net 4, le TPL est la méthode suggérée. Le modèle commun que j'ai vu recommandé est:
Task.Factory.StartNew(() => { DoSomething(); });
Cependant, la StartNew()
méthode retourne un Task
objet qui implémente IDisposable
. Cela semble être négligé par les personnes qui recommandent ce modèle. La documentation MSDN sur la Task.Dispose()
méthode dit:
"Appelez toujours Dispose avant de libérer votre dernière référence à la tâche."
Vous ne pouvez pas appeler dispose sur une tâche jusqu'à ce qu'elle soit terminée, donc avoir le thread principal attendre et appeler dispose annulerait le point de faire sur un thread d'arrière-plan en premier lieu. Il ne semble pas non plus y avoir d'événement terminé / terminé qui pourrait être utilisé pour le nettoyage.
La page MSDN sur la classe Task ne commente pas cela, et le livre "Pro C # 2010 ..." recommande le même modèle et ne fait aucun commentaire sur l'élimination des tâches.
Je sais que si je le laisse simplement, le finaliseur l'attrapera à la fin, mais est-ce que cela va revenir et me mordre quand je fais beaucoup de feu et que j'oublie des tâches comme celle-ci et que le fil du finaliseur sera débordé?
Donc mes questions sont:
- Est-il acceptable de ne pas faire appel
Dispose()
à laTask
classe dans ce cas? Et si oui, pourquoi et y a-t-il des risques / conséquences? - Y a-t-il une documentation qui en parle?
- Ou y a-t-il un moyen approprié de disposer de l'
Task
objet que j'ai manqué? - Ou existe-t-il une autre façon de faire feu et oublier les tâches avec le TPL?
la source
Réponses:
Il y a une discussion à ce sujet dans les forums MSDN .
Stephen Toub, membre de l'équipe Microsoft pfx a ceci à dire:
Mise à jour (octobre 2012)
Stephen Toub a publié un blog intitulé Dois-je me débarrasser des tâches? qui donne plus de détails et explique les améliorations de .Net 4.5.
En résumé: vous n'avez pas besoin de vous débarrasser des
Task
objets 99% du temps.Il y a deux raisons principales pour supprimer un objet: pour libérer des ressources non gérées de manière déterministe et opportune, et pour éviter le coût de l'exécution du finaliseur de l'objet. Aucun de ces éléments ne s'applique à la
Task
plupart du temps:Task
alloue le handle d'attente interne (la seule ressource non gérée dans l'Task
objet) est lorsque vous utilisez explicitement leIAsyncResult.AsyncWaitHandle
deTask
, etTask
objet lui-même n'a pas de finaliseur; le handle est lui-même enveloppé dans un objet avec un finaliseur, donc à moins qu'il ne soit alloué, il n'y a pas de finaliseur à exécuter.la source
EndInvoke
dans WinForms lors de l'utilisationBeginInvoke
pour exécuter du code sur le thread d'interface utilisateur). (2) Stephen Toub est assez bien connu en tant qu'orateur régulier sur l'utilisation efficace de PFX (par exemple sur channel9.msdn.com ), donc si quelqu'un peut donner de bons conseils, c'est bien. Notez son deuxième paragraphe: il y a des moments où laisser les choses au finaliser c'est mieux.C'est le même type de problème qu'avec la classe Thread. Il consomme 5 descripteurs de système d'exploitation mais n'implémente pas IDisposable. Bonne décision des concepteurs d'origine, il existe bien sûr peu de moyens raisonnables d'appeler la méthode Dispose (). Vous devrez d'abord appeler Join ().
La classe Task ajoute un handle à cela, un événement de réinitialisation manuelle interne. Quelle est la ressource de système d'exploitation la moins chère qui soit. Bien sûr, sa méthode Dispose () ne peut libérer qu'un seul descripteur d'événement, pas les 5 descripteurs consommés par Thread. Ouais, ne t'en fais pas .
Attention, vous devriez être intéressé par la propriété IsFaulted de la tâche. C'est un sujet assez moche, vous pouvez en savoir plus à ce sujet dans cet article de MSDN Library . Une fois que vous avez géré cela correctement, vous devriez également avoir une bonne place dans votre code pour éliminer les tâches.
la source
Thread
dans la plupart des cas, elle utilise le ThreadPool.J'adorerais voir quelqu'un peser sur la technique présentée dans cet article: Invocation de délégué asynchrone Typeafe Fire-and-forget en C #
Il semble qu'une méthode d'extension simple gérera tous les cas triviaux d'interaction avec les tâches et pourra appeler disposer dessus.
la source
Task
instance renvoyée parContinueWith
, mais voir la citation de Stephen Toub est la réponse acceptée: il n'y a rien à éliminer si rien n'effectue une attente bloquante sur une tâche.Task disper = null; disper = tsk.ContinueWith(cnt => { cnt.Dispose(); disper.Dispose(); });