Dot.Blog

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

Task, qui es-tu ? partie 7

La patience est une qualité parait-il, savoir attendre qu’une tâche se termine doit donc rendre le code meilleur… C’est ce que nous allons voir aujourd’hui avec l’art d’attendre la fin d’une Task !

Liens rapides vers la série complète

Attendre…

L’art de l’asynchronisme tient bien plus dans la façon d’attendre que d’exécuter du code. Ou plutôt dans l’art d’attendre pour gagner du temps utilisable autrement…

L’art du parallélisme est au contraire de ne pas attendre ou plutôt l’art de donner l’impression qu’il n’y a pas d’attente en utilisant les attentes créées par l’asynchronisme…

Les deux mécanismes sont donc complémentaires et se retrouvent par un heureux hasard, au sein de la classe Task.

Attendre qu’une tâche soit terminée oblige le code qui attend à ne plus rien faire, c’est bloquant. Le thread du code qui attend est suspendu au retour du code appelé. C’est pour cela que l’attente d’une Task est réservée aux Delegate Tasks et qu’elle ne s’utilise jamais avec les Promise Tasks.

Wait

La méthode Wait est d’une grande simplicité. Elle force l’attente d’une tâche, elle crée donc un point de blocage dans le thread courant jusqu’à ce que le thread de la tâche se termine.

void Wait();
void Wait(CancellationToken);
bool Wait(int);
bool Wait(TimeSpan);
bool Wait(int, CancellationToken);

 

Les différentes variantes de Wait déclinent les quelques possibilités mise à disposition pour gérer l’attente. L’attente se poursuit jusqu’à ce la tâche soit terminée quel que soit la façon dont cela arrive : fin normale, annulation, erreur.

L’annulation de tâche lève une exception OperationCanceledException. En revanche si un timeout a été programmé et que celui-ci est atteint Wait retournera la valeur False.

Si l’arrêt est lié à des erreurs d’exécution elles sont rassemblée par Wait dans une AggregateException.

Wait pourrait sembler une bonne solution pour rendre synchrone un code qui ne l’est pas. Ce n’est pas le bon outil pour cela … Wait s’utilise en réalité rarement. L’occasion la plus évidente est l’exécution d’un code asynchrone par la méthode Main d’une application. Dans ce cas il est évident qu’un Wait est utile puisque sinon Main se terminera ce qui mettra fin à l’application immédiatement… C’est pour cela qu’une méthode Main ne peut pas être async on plus, cela n’aurait aucun sens. Et comme elle ne peut pas être async elle ne peut pas utiliser await. C’est donc tout naturellement qu’on utilisera Task.Wait dans ce contexte précis.

Mais sinon les occasions d’utiliser Wait sont rares car pour tout code qui en attend un autre la meilleure approche est celle de async/await. Une bonne raison de plus à cela : Wait peut créer des deadlocks.

Mais await aussi si on n’y prend garde ! La raison tient en mot : le contexte. C’est un sujet important et je vous en reparlerais plus tard car je veux garder le fil…

WaitAll

On retrouve ici les mêmes possibilités que pour Wait :

static void WaitAll(params Task[]);
static void WaitAll(Task[], CancellationToken);
static bool WaitAll(Task[], int);
static bool WaitAll(Task[], TimeSpan);
static bool WaitAll(Task[], int, CancellationToken);

 

WaitAll est identique à Wait donc son principe et dans son fonctionnement sauf, on l’a compris, il s’agit ici d’attendre de façon groupée la fin d’un ensemble de tâches passées en paramètre.

WaitAll reste d’une utilisation assez rare. Cela peut être utile dans certains cas avec plusieurs Delegate Tasks exécutées en parallèle, mais ce n’est pas un cas si fréquent que cela. Le traitement parallèle des données est plus efficace avec PLINQ notamment. Et dans les cas où cela n’est pas adapté il est préférable de créer des tâches enfants qui ont des liens sémantiques entre elles plutôt que d’attendre un groupe de tâches créé de façon plutôt artificielle.

WaitAny

WaitAny se présente avec les mêmes variantes que WaitAll. La différence se joue dans ce qui est attendu. Avec WaitAll on attend que toutes les tâches soient terminées, avec WaitAny on sort de l’attente dès qu’une tâche se termine.

Si les cas d’utilisation de WaitAll sont rares, ceux de WaitAny le sont encore plus !

AsyncWaitHandle

Le type Task implémente l’interface IAsyncResult pour des raisons de compatibilité avec l’ancien code asynchrone basé sur APM comme nous l’avons vu dans une partie précédente. De fait Task propose aussi une propriété WaitHandle.

A moins d’avoir une grande quantité de code existant utilisant WaitHandle et de vouloir le maintenir en conservant la même logique AsyncWaitHandle n’a aucun intérêt.

Conclusion

Attendre n’est donc pas si simple… Et à moins d’avoir de bonnes raisons de les utiliser finalement aucune des méthodes de type Wait ne servent dans un code récent et bien écrit. Tout juste Task.Wait peut-il trouver quelques rares utilisations justifiées.

La prochaine partie s’intéressa notamment au résultat de Task<T>.

 

Stay Tuned !

blog comments powered by Disqus