La programmation asynchrone est de plus en plus utilisée aujourd’hui, autant pour améliorer les performances que pour améliorer la « fluidité » des applications.
Au fil des années, le Framework .NET s’est enrichi de nouvelles fonctionnalités facilitant l’accès à la programmation asynchrone. L’arrivée des mots clés « async » et « await » dans le Framework 4.5 ont grandement simplifié son utilisation.
Dans cet article, nous allons étudier un exemple simple de programmation asynchrone, avec affichage de la progression. Au sein d’une application Winform C#.NET, il s’agit d’effectuer un traitement quelconque (ici attendre 10 secondes), sans bloquer l’interface utilisateur. Une textbox « textbox1 » nous servira pour afficher la trace.
Appel synchrone
Dans notre écran, lançons pour commencer notre traitement de manière classique, c’est-à-dire en « synchrone ».
Résultat :
- Lors du click sur le bouton, l’interface utilisateur est bloquée pendant 10 secondes. Pendant ce laps de temps, l’utilisateur n’a plus la main dans l’application, le code attend la fin de l’exécution de la procédure « AttendreDixSecondes» avant de passer à l’instruction suivante.
- Une fois les 10 secondes écoulées, le message « Suite procédure principale » s’affiche.
- Notre « textbox1 » affiche donc, dans cet ordre :
- Début procédure principale
- Début traitement
- Fin traitement (au bout de 10 secondes de "blocage")
- Suite procédure principale
Appel asynchrone
Lançons maintenant le même traitement de manière asynchrone.
Le click du bouton appelle maintenant la méthode LancerTraitementAsync :
Cette méthode « LancerTraitementAsync » appelle notre fonction « AttendreDixSecondes» de manière asynchrone :
On notera qu’elle possède dans sa définition le mot clé « async » (lancement en asynchrone), ainsi qu’une instruction « await » indiquant, au sein de cette fonction asynchrone, d’attendre la fin de l’exécution de la méthode « AttendreDixSecondes» avant de continuer.
La méthode « Attendre 10 secondes » reste la même :
Résultat :
- Lors du click sur le bouton, on appelle la méthode «LancerTraitementAsync » : celle-ci affiche le message « Début traitement » dans la textbox, puis attend 10 secondes.
- «LancerTraitementAsync » étant lancée de manière asynchrone, l’interface utilisateur n’est pas bloquée : la méthode Click continue de se dérouler : le message « Suite procédure principale» s’affiche sans attendre les 10 secondes.
- A l’issue des 10 secondes, la méthode «LancerTraitementAsync » affiche le message « Fin traitement ».
- Au final, la texbox affiche, dans cet ordre :
- Début procédure principale
- Début traitement
- Suite procédure principale
- Fin Traitement (au bout de 10 secondes, sans avoir "bloqué" l'écran)
- Par rapport à l’appel synchrone, on constate que la procédure principale n’a pas attendu la fin du traitement pour continuer
En imaginant que le traitement « Attendre10Secondes » corresponde à un chargement de données quelconque, nous avons amélioré la navigation dans l’application en ne bloquant pas l’utilisateur pendant ce temps de chargement. Mieux, nous allons maintenant afficher la progression du chargement, toujours sans bloquer l’utilisateur.
Appel asynchrone avec progression
Depuis le Framework 4.5, l’interface Iprogress<T> a été ajoutée pour informer de l’avancement d’un traitement. Cette interface ne contient qu’un seule méthode : Report<T>, appelée au sein des méthodes asynchrones.
Dans notre exemple, nous allons utiliser cette interface avec un type “entier” pour informer du pourcentage de progression de l’attente.
La méthode « AttendreDixSecondes » doit disposer d’un paramètre de type IProgress<int>, par lequel on fera transiter l’information d’avancement avec la méthode Report() :
Au sein de la méthode “lancerTraitementAsync” on initialise une variable « progress » du même type.
On définit par une lambda expression l’action à réaliser quand une information de progression est reçue : ici nous allons afficher le pourcentage d’attente dans notre textbox1, mais la plupart du temps nous utiliserons une barre de progression.
Résultat :
- La texbox affiche, dans cet ordre :
- Début procédure principale
- Début traitement
- Suite procédure principale
- Attente : 10 %
- Attente : 20 %
- Attente : 30 %
- Attente : 40 %
- Attente : 50 %
- Attente : 60 %
- Attente : 70 %
- Attente : 80 %
- Attente : 90 %
- Attente : 100 %
- Fin traitement
- Nous obtenons le même résultat que précédemment, avec en plus, une progression de l’avancement (s’incrémentant de 10% à chaque seconde d’attente).
- Pendant ce temps, l’interface utilisateur n’est pas bloquée, l’utilisateur a toujours la main pour cliquer où il le souhaite, ce qui améliore grandement le confort d’utilisation.
Remarque : progression dans l’UI par lambda expression
Il aurait été impossible de mettre à jour la textbox (ou la progressbar) directement depuis la fonction AttendreDixSecondes, car celle-ci est lancée de manière asynchrone donc dans un Thread différent de celui gérant l’interface utilisateur. Nous aurions obtenu le message suivant :
Le fait d’utiliser une lambda expression permet de capter le contexte « UI », et de gérer l’affichage de la progression dans le même contexte que l’interface utilisateur.