Dot.Blog

C#, XAML, WinUI, WPF, Android, MAUI, IoT, IA, ChatGPT, Prompt Engineering

Task, qui es-tu ? partie 5

[new:30/09/2015]Nous allons continuons à explorer les différentes propriétés de Task, aujourd’hui examinons le Status d’une tâche.

Liens rapides vers la série complète

Status

Cette propriété doit être comprise comme l’état de la mini machine à états qu’est une Task. On pourra relire à ce sujet un article récent qui portait justement ce sujet (les machines à états finis).

Savoir dans quel état se trouve une tâche est une information qui peut avoir son importance dans certains cas. Pour les systèmes de haut niveau que nous utilisons pour créer des Task il est certains que cela est utile, pour le code que nous écrivons l’importance est moindre.

TaskStatus Status { get; }

 

Toutefois comme l’état change de valeur dans des cas un peu différents selon qu’on utilise une Promise Task ou une Delegate Task et que cette nuance fondamentale doit être toujours présente à l’esprit quand on parle de Task, il est intéressant d’aller voir cela plus en profondeur…

Le cas des Delegate Tasks

Les différents états d’une tâche en mode Delegate Task sont représentés ci-dessous :

image

 

Le plus souvent les Delegate Tasks sont créées en utilisant Task.Run (ou Task.Factory.StartNew) et entrent immédiatement dans l’état WaitingToRun. Ce qui signifie que la tâche est créée et planifiée par le scheduler et qu’elle attend simplement son tour.

Les états en gris représentent des états qu’on ne voit que lorsqu’on utilise les constructeurs de Taks, ce qui est rarissime comme nous avons pu le voir. Par exemple après l’appel d’un constructeur l’état, en toute logique, devient Created. Une fois la tâche assignée à un scheduler son état passe à WaitingToRun (en utilisant Start ou RunSynchronously).

Si la tâche est la continuité d’une autre tâche alors elle démarre en mode WaitingForActivation et bascule automatiquement vers WaitingToRun quand la tâche qui la précède se termine.

L’état Running est évident : le code de la Delegate Task est en cours d’exécution.

Quand ce code a terminé son exécution la tâche passe si besoin par un état d’attente de ses tâches enfants, ce qui est matérialisé par l’état WaitingForChildrenToComplete. On remarque au passage qu’un librairie qui utilise des noms assez longs et assez clairs se passent de documentation assez facilement…

A la fin de cycle la tâche peut prendre trois statuts différents : RanToCompletion qui indique que tout s’est bien passé, Faulted qu’on comprend comme l’indication qu’une erreur est survenue dans le mécanisme, et Canceled si la tâche a été volontairement stoppée.

Il est bien entendu assez rare de pouvoir suivre tous ces états en temps réel, tout va très vite et certains états sont franchis avant même qu’on puisse lire l’état de Status. De même une annulation de la tâche pourra la faire passer à Canceled avant même qu’elle passe par Running.

Donc lire Status peut avoir un intérêt dans quelques cas mais c’est surtout l’énoncé de ses valeurs possibles qui est intéressant pour comprendre le mécanisme des Task.

Le cas des Promise Tasks

Rappelez-vous que les Promise Task ne sont en réalité pas affectée à un thread et qu’il n’y a pas de code qui tourne, ce sont juste des évènements. Tout naturellement les états possibles reflète cette nature totalement différente bien qu’il s’agisse toujours de la classe Task…

image

 

Comme il ne s’agit pas de représenter tous les diagrammes UML de Task il y a par force des simplifications. Les Promise Tasks peuvent éventuellement passer par l’état WaitingForChildrenToComplete notamment. Mais cela est tellement bizarre que les tâches créées par async le sont avec le flag DenyChildAttach, rejetant ainsi la possibilité d’accrocher des tâches enfants.

On pourrait se demander malgré tout pourquoi il n’y a pas d’état Running. Après tout lorsqu’on attend qu’une requête HTTP soit “exécutée” il se passe bien quelque chose. Oui mais cette “exécution” se passe ailleurs ! Pour votre code il ne se passe rien d’autre qu’une attente. Aucun code ne tourne sur aucun cœur du CPU…

Cette dualité Delegate / Promise Task est déroutante comme j’ai eu l’occasion de le présenté dans l’une des premières parties de cette série. Mais justement, il faut la garder sans cesse présent à l’esprit pour comprendre et utiliser Task correctement.

Les autres propriétés d’état

Task réserve des surprises… Status est un état unique, celui d’une machine à états finis qui par définition ne peut avoir qu’un seul état à la fois. Mais pour des raisons que nous ne discuterons pas ici Task offre d’autres propriétés de type booléen qui se rapprochent de certaines valeurs de Status :

bool IsCompleted { get; }
bool IsCanceled { get; }
bool IsFaulted { get; }

 

On comprend facilement que lorsque IsCanceled passe à True cela correspond à l’état Canceled. Il en va de même pour IsFaulted et l’état Faulted.

Mais pour IsCompleted c’est plus subtile il n’y a pas de correspondance directe avec RanToCompletion comme on pourrait s’y attendre.

IsCompleted passe à True quand la tâche passe à n’importe lequel des états “terminaux”, c’est à dire aussi bien à RanToCompletion qu’à Canceled ou Faulted !

Conclusion

L’étude des propriétés de Task n’est pas la partie la plus exaltante mais elle est instructive et réserve parfois des surprises comme IsCompleted. Des subtilités qui hélas n’apparaissent pas de façon évidente sans les étudier même rapidement comme nous le faisons ici.

Mais la visite n’est pas terminée, attendons la partie 6 qui nous présentera les propriétés Id et CurrentId.

Stay Tuned !

blog comments powered by Disqus