Comprendre Async (deuxième étape)

Contexte

Dans l’article précédent, j’ai déroulé mes premiers tests en Async, histoire de voir si l’apprentissage par l’erreur pouvait aider à comprendre comment fonctionne cette technologie. Nous nous étions quittés sur une question, à savoir s’il y avait plus simple / élégant / recommandé pour réaliser une exécution de code lors de la fin d’un appel asynchrone. La méthode asynchrone renvoyant au final une valeur, nous souhaitions pouvoir par exemple utiliser la valeur pour appeler une autre fonction, sans nécessairement compliquer le code avec des ContinueWith.

On continue de creuser

D’après ce que je lis ça et là sur le sujet, il doit être possible d’écrire cette ligne plus simplement :

image

On va tenter l’écriture avec un await :

image

Problème habituel : on ne peut pas mettre un await sans que la fonction soit déclarée en async (ça commence à rentrer…) et on essaie donc de mettre un async sur le Main, avec comme un vieux doute :

image

Effectivement, ça plante :

image

Ce n’est pas grave : on va juste extraire la méthode (en utilisant la refactorisation de Visual Studio bien sûr) pour la mettre dans une fonction async :

image

On donne le nom d’Appel à la fonction à créer, et on obtient un code qui compile sans erreur :

image

Par contre, il y a quand même un avertissement :

image

Effectivement, si on exécute tel quel, on a toujours le même souci, à savoir que comme on va arrêter la thread Main avant que le message en retour ne soit traité, on ne verra pas la fin du traitement :

image

Pour valider que c’est bien le cas, on ressort notre astuce précédente avec un Thread.Sleep(3000) dans le Main :

image

Le résultat montre que dès le premier affichage de res, on a maintenant une valeur :

image

Du coup, pas besoin de boucler pour attendre qu’il soit différent de la valeur par défaut, et on peut écrire simplement :

image

Mais du coup, on retombe finalement avec ce genre d’appel sur un enchaînement synchrone…

Point à mi-parcours (OK, à 10% du parcours)

En fait, arrivé là, je me demande si le problème ne vient pas de ce que j’essaie de comprendre l’asynchronisme à partir d’une application en mode console, qui fonctionne de façon séquentielle, et donc fondamentalement synchrone. Du coup, je vais plutôt continuer avec une application test Winforms, histoire de voir les effets sur une application qui utilise une pompe de message. L’hypothèse est que, comme l’await dans notre cas revient au comportement synchrone, il doit quand même y avoir une différence, mais elle n’est pas visible…

Quelques semaines plus tard

Arrivé à cette étape, je me suis rendu compte que j’avais besoin de lire de la doc avant de continuer à faire mes tests en aveugle depuis le code, sous peine de ne pas arriver à grand chose. Entretemps, une lecture du blog de Julien Dollon (http://julien.dollon.net/post/Async-is-not-ALWAYS-your-friend.aspx) et un commentaire de sa part m’ont aidé à avancer en partageant l’asynchronisme et la concurrence par les tâches.

Cette différence, fondamentale mais pas si facile à capter, est bien posée dans l’excellent blog de Jérôme Laban, dans la partie 1 de ses posts sur Async. Je vous mets les liens, parce que ce sont trois très bons articles :

http://www.jaylee.org/post/2012/06/18/CSharp-5-0-Async-Tips-and-Tricks-Part-1.aspx

http://www.jaylee.org/post/2012/07/08/c-sharp-async-tips-and-tricks-part-2-async-void.aspx

http://www.jaylee.org/post/2012/09/29/C-Async-Tips-and-Tricks-Part-3-Tasks-and-the-Synchronization-Context.aspx

Le premier lien m’a permis de comprendre qu’effectivement, je ne pourrais pas aller beaucoup plus loin dans la compréhension d’Async avec une application console. L’auteur explique qu’Async ne s’occupe pas de concurrence, mais simplement d’asynchronisme par la mise en place (par le compilateur) d’une machine à état, qui fait progresser les différentes étapes par les mots clés await. Hors, une des difficultés est justement que la synchronisation du contexte ne donne pas la garantie de retomber sur le thread UI, ou sur le thread nécessaire pour que des opérations dépendantes du thread fonctionnent.

Bref, ça conforte l’idée qu’il faut repartir d’un autre exemple avec une interface, ou en tout cas deux threads. C’est d’une certaine manière ce qui a forcé la mise en place de Task dans les exemples précédents, alors que les notions sont séparées.

Cet article a également participé à me faire comprendre qu’Async ne mettait pas fondamentalement en place de la concurrence, car il fonctionne à la base sur un thread.

Quelques notes de plus

En fait, await n’attend pas tout de suite, mais il est le signal que la tâche représentée par la fonction sera considérée comme terminée lorsqu’elle aura fini la commande marquée comme await.

public async Task Initialize()   
            {       
                //Thread.Sleep(500);
                await Task.Delay(500);
            }

Thread.Sleep fait attendre tout le monde. Task.Delay sans await lance un delai, mais on passe 3 ms au final, car la fin n’est jamais attendue. Quand on met un await, on n’attend pas la fin de chaque tâche, mais on donne la possibilité au cas appelant de connaître la fin de ces tâches, bien qu’elles aient été lancées à la suite sans délai. Du coup, on est à 503 ms…

Au programme de la troisième étape

Dans le prochain article sur le sujet, je repartirai d’une application exemple qui va nous permettre de mieux voir le fonctionnement que mon application console, pas adaptée. Pour ceux qui souhaitent comprendre Async, désolé pour ce détour, mais je vous avais prévenu : ces articles ne sont pas des cours par quelqu’un qui connaît, mais un carnet de bord des étapes d’apprentissage de quelqu’un qui cherche à apprendre. C’est du coup plus tortueux, mais j’espère que ça adoucit la courbe d’apprentissage pour un débutant…

En attendant, je vous recommande à nouveau la lecture des trois articles sur le blog de Jérôme Laban. Perso, j’y retourne…

About JP Gouigoux

Jean-Philippe Gouigoux est Architecte Logiciel, MVP Connected Systems Developer. Il intervient régulièrement à l'Université de Bretagne Sud ainsi qu'à l'Agile Tour. Plus de détails sur la page "Curriculum Vitae" de ce blog.
This entry was posted in .NET and tagged . Bookmark the permalink.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Captcha Captcha Reload