Programmation C sharp/Fonctions asynchrones
L'appel à une fonction se fait de manière synchrone : aucune autre instruction n'est exécutée avant que la fonction ne retourne une valeur. Cependant certaines fonctions prennent beaucoup de temps, en particulier les opérations d'entrées-sorties, et les fonctions de communication par réseau informatique.
Pour ce genre de fonction, l'API .Net possède souvent deux versions de la méthode :
- Une méthode synchrone qui attend la fin de l'opération avant d'en retourner le résultat,
- Une méthode asynchrone demandant au pool de threads standard d'effectuer l'opération, puis retourne immédiatement à la méthode appelante.
Syntaxe et utilisation des méthodes asynchrones
[modifier | modifier le wikicode]Une méthode synchrone est déclarée selon la syntaxe suivante :
type_retour nom_methode ( arguments... )
La version asynchrone utilise deux autres méthodes. La première commence par Begin
et demande au pool de threads standard d'effectuer l'opération. Une fois l'opération terminée, un delegate est appelé. Celui-ci appelle alors la méthode dont le nom commence par End
pour récupérer la valeur retournée par la méthode asynchrone.
Ces deux méthodes ont la syntaxe suivante :
IAsyncResult Beginnom_methode ( arguments_non_ref..., AsyncCallback callback, object state ) type_retour Endnom_methode ( IAsyncResult, arguments_ref )
Les arguments de la méthode synchrone se retrouvent répartis entre les deux méthodes :
- Les paramètres passés par valeur sont passés à la méthode
Begin
, - Les paramètres de sorties (out et ref) sont passés à la méthode
End
.
La méthode Begin
possède deux arguments supplémentaires :
- Un delegate de type AsyncCallback appelé quand l'opération est terminée (celui-ci doit alors appeler la méthode
End
correspondante), - Un objet à transmettre au delegate.
Le delegate AsyncCallback
[modifier | modifier le wikicode]La syntaxe du delegate AsyncCallback
est la suivante :
public delegate void AsyncCallback(IAsyncResult result)
Le paramètre result
est une interface de type IAsyncResult
correspondant à celui retourné par la méthode Begin
, et doit être passé à la méthode End
.
L'interface IAsyncResult
[modifier | modifier le wikicode]L'interface IAsyncResult
possède les propriétés en lecture seule suivantes :
- Propriété généralement utilisée par le delegué appelé quand l'opération est terminée :
- object AsyncState
- Cet objet correspond à celui transmis à la méthode Begin.
- Propriétés généralement utilisées par le code appelant la méthode Begin de l'opération asynchrone :
- WaitHandle AsyncWaitHandle
- Cet objet de synchronisation est mis dans l'état signalé quand l'opération est terminée.
- bool IsCompleted
- Ce booléen vaut true (vrai) lorsque l'opération est terminée.
- bool CompletedSynchronously
- Ce booléen vaut true (vrai) si l'opération s'est terminée de manière synchrone.
La propriété AsyncWaitHandle
permet de lancer une opération asynchrone, d'effectuer d'autres traitements durant l'opération en cours, et finalement attendre la fin de l'opération si elle ne s'est pas déjà terminée, en testant la propriété IsCompleted
.
Fonctions asynchrones et tâches
[modifier | modifier le wikicode]Depuis C#5.0, il est possible de simplifier la programmation asynchrone avec les mots-clés async
et await
.
Déclaration d'une fonction asynchrone
[modifier | modifier le wikicode]Le mot-clé async
est utilisé dans la déclaration d'une méthode pour indiquer qu'elle est asynchrone.
Cela signifie que son exécution comporte une longue tâche, et qu'elle peut utiliser le mot-clé await
pour attendre une autre tâche.
Le type de sa valeur de retour T
est modifié en Task<T>
indiquant que la fonction asynchrone retourne désormais une tâche dont le résultat est de type T
.
Par convention, le nom d'une fonction asynchrone se termine par "Async".
Exemple :
async Task<string> getPageContentAsync(string url)
{
string page_content = null;
// ... connexion, téléchargement, ...
// page_content = ...
return page_content;
}
Appeler et attendre une tâche
[modifier | modifier le wikicode]Appeler une fonction asynchrone est une façon de créer une nouvelle tâche :
async void test()
{
Console.WriteLine("Téléchargement des pages...");
Task<string> tache = getPageContentAsync("https://fr.wikibooks.org/w/Programmation_C_sharp/Fonctions_asynchrones");
Console.WriteLine("Le programme peut faire autre chose pendant le téléchargement...");
Console.WriteLine("Là on attend pour utiliser le résultat :");
string page = await tache;
Console.WriteLine("La page contient : ");
Console.WriteLine(page);
}
L'appel de l'exemple précédent se fait de manière asynchrone, récupérant un objet tâche.
La méthode peut faire autre chose avant d'attendre et récupérer le résultat explicitement en utilisant await
.
L'utilisation du mot-clé await
dans une méthode n'est autorisé que si la méthode est déclarée elle-même asynchrone avec async
.
La hiérarchie des appels est donc une chaîne de méthodes asynchrones (async -> attendre avec await, donc méthode async, ...), qui peut toutefois s'arrêter soit en n'utilisant pas le mot-clé await
si on n'a pas besoin d'attendre la fin, soit en utilisant la méthode Wait()
de la classe Task
comme dans cet exemple :
void test()
{
Console.WriteLine("Téléchargement des pages...");
Task<string> tache = getPageContentAsync("https://fr.wikibooks.org/w/Programmation_C_sharp/Fonctions_asynchrones");
Console.WriteLine("Le programme peut faire autre chose pendant le téléchargement...");
Console.WriteLine("Là on attend :");
tache.Wait();
Console.WriteLine("Fin de tâche");
}
Créer une tâche
[modifier | modifier le wikicode]La classe Task
possède des méthodes statiques pour créer des tâches :
- La méthode
Task.Run( function() )
crée une tâche pour exécuter la fonction donnée en argument. - La méthode
Task.Wait( délai_en_millisecondes )
crée une tâche pour attendre le nombre de millisecondes spécifié.
Exemple pour simuler le téléchargement de page des exemples précédents :
async Task<string> getPageContentAsync(string url)
{
string page_content = "<<LOADING...>>";
// Utilisation de la notation (arguments) => {code}
await Task.Run(() =>
{
// ... connexion, téléchargement, ...
Console.WriteLine("* Loading...");
Task.Delay(2000).Wait();
page_content = "<<Contenu de la page "+url+">>";
Console.WriteLine("* Loaded");
});
Console.WriteLine("* Return");
return page_content;
}