Dot.Blog

C#, XAML, Xamarin, UWP/Android/iOS

Task, qui es-tu ? partie 1

[new:30/09/2015]J’ai souvent parlé de parallélisme, de PLINQ, de multitâche, d’async/await, mais je me rend compte que Task est la grande oubliée de tout ça. Corrigeons cela par une petite série d’articles !

Liens rapides vers la série complète

Task la fuyante…

En faisant le tour de ce que j’avais dit sur la question je suis me suis aperçu que la classe Task (et Task<T>) était un peu les oubliées de tous ces brillants (soyons modeste!) exposés sur le parallélisme, le multitâche et l’asynchronisme (autant de notions différentes dont vous savez tout j’en suis certain, sinon relisez Dot.Blog Sourire).

Lors de cette prise de conscience je me suis aperçu que Task était souvent négligée. Peut-être en raison d’une disposition particulière liée à son apparition au milieu de plein d’autres choses qui semblent parler de la même chose.

Ainsi il existe chez les développeurs deux approches très tranchées :

  • Ceux qui ont déjà utilisé Task et la TPL (dont j’ai parlé en son temps) – Task Parallel Library – depuis sont introduction dans .NET 4.0. Ces développeurs là sont familiers avec la classe Task et son utilisation au sein de la mécanique subtile qu’est le parallélisme. Un danger : celui encouru par tous les early adopters… C’est qu’on croit savoir puisqu’on a même fait partie des premiers, et du coup on est à côté de ses pompes dans le présent car beaucoup de choses ont changée et qu’on ne s’est pas donné la peine de tout revoir… or Task de TPL est très différent du Task utilisé aujourd’hui avait await notamment.
  • Ceux qui n’ont jamais entendu parlé de Task jusqu’à l’apparition de async dans C#. Un danger : pour eux Task n’est qu’une partie accessoire participant à la mise en œuvre de async/await. Un truc de plus, compliqué en plus, à apprendre. La prise de conscience d’une continuité est absente… Chaque méthode Task est considérée comme faisant partie de ce tout orienté async/await alors qu’il n’en est rien.

 

Bien entendu il y a le troisième groupe qui n’a jamais ni utilisé ni entendu parlé de TPL et encore moins de async/await, mais là ça doit être des développeurs C++ ou Java ou un truc comme ça, ça compte pas…

L’équipe de développement qui a introduit “async” et ses mécanismes a été tenté pendant un temps de créer sa propre classe mais réutiliser Task a fini par l’emporter pour de nombreuses raisons et certainement la plus importante : elle existait et elle pouvait faire le job sans multiplier les solutions proches et confusantes. Ainsi il fut décider d’étendre Task pour satisfaire les besoins de async. Créant de la confusion en voulant en éviter…

Ainsi la classe Task contient des choses qui ne servent à rien à la logique async et qui peuvent rendre perplexes les développeurs du second groupe évoqué plus haut, autant qu’elle peut sembler être la même qu’à l’époque de TPL et être utilisée de la même façon ce qui mène à des déconvenues et à du mauvais code chez les développeurs du premier groupe…

Les deux faces d’une tâche

Donc depuis “async” Task est un même noyau avec deux visages, à la façon de la tête d’un Terminator un peu esquinté…

On peut même dire qu’il y a deux types de tâches. Le premier type est celui des “delegate tasks”. Ce sont des tâches qui proposent un code à faire tourner. Le second type est appelé “promise tasks”, ce sont des tâches qui représentent des évènements ou des signaux. Elles sont souvent basées sur des entrées/sorties comme un évènement de fin de téléchargement HTTP par exemple. Mais elle peuvent servir à plein de choses très différentes comme la fin d’un timer de n secondes.

Dans le monde de la TPL les tâches étaient toutes, ou presque, de type “Delegate Task”. C’est-à-dire des tâches possédant un code à faire tourner. La TPL offrait un support minimaliste des “promise tasks”.

On notera que j’ai abandonné l’idée de traduire les termes “Delegate task” et “Promise task”, le lecteur m’en excusera j’en suis certain d’autant que je pense que cela lui sera plus profitable et plus clair d’utiliser ces termes qu’il rencontrera ailleurs plutôt que d’inventer des traductions farfelues qui ne l’éclaireront pas dans ces futures lectures…

Quand il y a des traitements parallèles les Delegate tasks sont distribuées sur plusieurs threads qui exécutent réellement le code de ces dernières. Dans le monde async la plupart des tâches sont des Promise task. Il n’y a aucun thread accroché à leur existence et qui attendrait que la tâche se termine…

Cette différence est tellement énorme qu’elle valait bien un billet d’introduction !

Vous pourrez parfois lire les termes de “code-based tasks” et de “event-based tasks” en place et lieu de “Delegate tasks” et “Promise tasks”. On comprend que l’esprit est le même, la terminologie est juste plus ancienne. Les tâches “code-based” sont celles qui contiennent du code à exécuter, comme un Delegate, alors que les tâches “event-based” sont conçues sur la notion d’évènement c’est à dire d’une promesse d’un quelque chose venir (“Promise task”, des “tâches promesses”).

Mais le plus essentiel de tout cela est bien de comprendre que les Delegate tasks contiennent du code à exécuter qui le sera dans un thread alors que les Promises tasks ne font qu’attendre un évènement et ne nécessitent aucun thread pour cela, en tout cas de façon obligatoire.

C’est en cela que “classe Task  = multitâche” est une confusion horrible qui interdit de comprendre et d’utiliser convenablement les tâches …

Je l’avais déjà dit dans mon billet “De la bonne utilisation de Async/Await en C#” (juin dernier) : “les threads ne sont pas des taches”, mais tout cela était orienté async/await et je m’aperçois que le message n’avait peut-être pas été énoncé avec assez d’emphase pour qu’il passe assurément !

Conclusion

Bien comprendre Task c’est déjà saisir cette nuance énorme entre Delegate Task et Promise Task, comprendre l’histoire d’une fusion entre TPL et async/wait qui peut créer de la confusion.

Nous verrons dans la seconde partie comment une tâche se construit…

Stay Tuned !

blog comments powered by Disqus