Comment fonctionnent les tâches asynchrones (Async/Await) dans .Net 4.5?
Quelques exemples de code:
private async Task<bool> TestFunction()
{
var x = await DoesSomethingExists();
var y = await DoesSomethingElseExists();
return y;
}
La deuxième instruction await
est-elle exécutée immédiatement ou après le retour de la première await
?
await
suspend la méthode jusqu'à la fin de l'opération. Ainsi, le second await
serait exécuté après le retour du premier await
.
Pour plus d'informations, consultez mon async
/await
intro ou FAQ officielle .
Il s'exécute après le premier retour en attente. Si cette chose vous déroute, essayez de jouer avec les points d'arrêt - ils sont entièrement pris en charge par le nouveau modèle asynchrone.
Imaginez que cela ressemble à ceci:
var x = await GetSomeObjectInstance();
var y = await GetSomeObjectInstance2(x);
Il se produirait probablement une exception NullReferenceException quelque part, donc la première attend doit retourner en premier. Sinon, x
serait nul/non défini ou autre.
Les appels de méthodes se produiront toujours de manière séquentielle, tout comme les appels de méthode "réguliers", non attendus. L'attente a pour but de renvoyer le thread actuel au pool de threads pendant que l'opération attendue s'exécute et fait tout.
Cela est particulièrement utile dans les environnements hautes performances, par exemple un serveur Web, où une demande donnée est traitée sur un thread donné à partir du pool de threads global. Si nous n'attendons pas, le thread donné traitant la demande (et toutes ses ressources) reste "en cours d'utilisation" pendant la fin de l'appel db/service. Cela peut prendre quelques secondes ou plus, en particulier pour les appels de service externes.
Maintenant, dans les sites Web à faible trafic, ce n'est pas vraiment un problème, mais dans les sites à fort trafic, le coût de tous ces threads de demande reste assis, ne fait rien, dans un état "en cours d'utilisation", en attendant d'autres processus comme ces appels de base de données/service revenir peut être un fardeau de ressources.
Il vaut mieux relâcher le thread dans le pool de travail pour lui permettre d'effectuer d'autres travaux utiles pour une autre demande.
Une fois l'appel db/service terminé, nous pouvons alors interrompre le pool de threads et demander à un thread de poursuivre le traitement de cette demande là où elle s'est arrêtée. À ce stade, l'état de la demande est rechargé et l'appel de méthode se poursuit.
Donc, sur une base par demande lors de l'utilisation de l'attente, la demande prendra toujours le même temps du point de vue des utilisateurs ... plus un tout petit peu plus pour la surcharge de commutation.
Mais dans l'ensemble, pour toutes les demandes de tous les utilisateurs, les choses peuvent sembler plus performantes pour tous les utilisateurs car le serveur Web (dans ce cas) fonctionne plus efficacement avec une meilleure utilisation des ressources. c'est-à-dire qu'il n'a pas à mettre en file d'attente les demandes en attente de threads libres pour traiter les demandes car attendre les renvoie ou bien nous n'avons pas à acheter plus de matériel car nous utilisons la même quantité de matériel, plus efficacement, pour obtenir plus débits.
Cependant, il y a un coût de commutation, donc malgré ce que vous voyez dans les modèles par défaut et dans de nombreux documents, vous ne devriez pas simplement utiliser aveuglément attendre pour chaque appel. C'est juste un outil et comme tous les outils, il a sa place. Si le coût de commutation n'est pas inférieur au coût de simplement terminer vos appels de manière synchrone, vous ne devriez pas utiliser attendre.