Je veux 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 méthode StartNew()
renvoie un objet Task
qui implémente IDisposable
. Cela semble être ignoré par les personnes qui recommandent ce modèle. La documentation MSDN sur la méthode Task.Dispose()
dit:
"Appelez toujours Dispose avant de libérer votre dernière référence à la tâche."
Vous ne pouvez pas appeler disposer sur une tâche jusqu'à ce qu'elle soit terminée, donc avoir le thread principal en attente et appeler disposer défait 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é pouvant être utilisé pour le nettoyage.
La page MSDN de la classe Task ne fait aucun commentaire à ce sujet 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, le finaliseur le rattrapera à la fin, mais est-ce que ça va revenir et me mordre quand je fais beaucoup de feu et oublier des tâches comme ça et le fil du finaliseur est débordé?
Mes questions sont donc:
Dispose()
sur la classe Task
dans ce cas? Et si oui, pourquoi et y a-t-il des risques/conséquences?Task
que j'ai manqué?Il y a une discussion à ce sujet dans les forums MSDN .
Stephen Toub, membre de l'équipe Microsoft pfx a ceci à dire:
Task.Dispose existe parce que Task peut potentiellement encapsuler un descripteur d'événement utilisé lors de l'attente de la tâche, dans le cas où le thread en attente doit réellement se bloquer (par opposition à la rotation ou potentiellement à l'exécution de la tâche en attente). Si tout ce que vous faites est d'utiliser des continuations, ce descripteur d'événement ne sera jamais alloué
...
il vaut probablement mieux compter sur la finalisation pour s'occuper des choses.
Mise à jour (octobre 2012)
Stephen Toub a publié un blog intitulé Dois-je disposer 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 disposer des objets Task
99% du temps.
Il y a deux raisons principales pour éliminer un objet: libérer des ressources non gérées de manière déterministe et en temps opportun, et éviter les coûts d'exécution du finaliseur de l'objet. Aucun de ces éléments ne s'applique à Task
la plupart du temps:
Task
alloue le handle d'attente interne (la seule ressource non gérée dans l'objet Task
) est lorsque vous utilisez explicitement le IAsyncResult.AsyncWaitHandle
du Task
, etTask
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.Il s'agit du 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 façons raisonnables d'appeler la méthode Dispose (). Vous devez d'abord appeler Join ().
La classe Task ajoute une poignée à 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 que l'un des descripteurs d'événement, pas les 5 descripteurs consommés par Thread. Ouais, ne vous embêtez pas .
Attention, vous devez être intéressé par la propriété IsFaulted de la tâche. C'est un sujet assez laid, vous pouvez en savoir plus à ce sujet dans cet article de la bibliothèque MSDN . Une fois que vous avez traité cela correctement, vous devriez également avoir une bonne place dans votre code pour disposer des tâches.