async wait Behavior [duplicate]
J'expérimentais avec async-await et je suis tombé sur ce comportement plutôt étrange, du moins pour moi.
J'ai créé trois méthodes qui simulaient des tâches de longue durée.
Considérez les deux gestionnaires de clic de bouton:
pour button1_click, le temps écoulé était d'environ 6000 ms tandis que button2_click était d'environ 3000 ms.
Je ne suis pas en mesure de comprendre pourquoi cela s'est produit, c'est-à-dire 6000 ms contre 3000 ms.
private async Task<string> TaskOne()
{
await Task.Delay(1000);
return "task one";
}
private async Task<string> TaskTwo()
{
await Task.Delay(2000);
return "task two";
}
private async Task<string> TaskThree()
{
await Task.Delay(3000);
return "task three";
}
//time elapsed = 6000+ms
private async void button1_Click(object sender, EventArgs e)
{
var watch = new Stopwatch();
watch.Start();
await TaskOne();
await TaskTwo();
await TaskThree();
watch.Stop();
textBox3.Text = watch.ElapsedMilliseconds.ToString();
}
//time elapsed = 3000+ms
private async void button2_Click(object sender, EventArgs e)
{
var watch = new Stopwatch();
watch.Start();
var taskOne = TaskOne();
var taskTwo = TaskTwo();
var taskThree = TaskThree();
await taskOne;
await taskTwo;
await taskThree;
watch.Stop();
textBox3.Text = watch.ElapsedMilliseconds.ToString();
}
Réponses
Dans ce cas:
await TaskOne();
await TaskTwo();
await TaskThree();
TaskTwo () ne peut pas démarrer tant que TaskOne () n'est pas terminé car vous l'attendez. De même, TaskThree () ne peut pas démarrer tant que TaskTwo () n'est pas terminé en raison de l'attente.
Ensuite:
var taskOne = TaskOne();
var taskTwo = TaskTwo();
var taskThree = TaskThree();
await taskOne;
await taskTwo;
await taskThree;
Vous commencez les 3 tâches en même temps et vous les attendez. C'est pourquoi cela ne prend que la durée de la tâche la plus longue. Vous seriez surpris de voir combien de personnes ne comprennent pas cette attente asynchrone. Si les tâches ne dépendent pas les unes des autres, c'est la voie à suivre.
Sommaire
Le point à retenir ici (et c'est une idée fausse très courante dans le cas contraire), c'est qu'en await
réalité cela signifie vraiment «attendre» .
Wait, opérateur (référence C #)
Soulignez le mien
L'opérateur await suspend l'évaluation de la méthode asynchrone englobante jusqu'à ce que l'opération asynchrone représentée par son opérande se termine . Une fois l'opération asynchrone terminée, l'opérateur await renvoie le résultat de l'opération, le cas échéant.
Lorsque l'opérateur await est appliqué à l'opérande qui représente une opération déjà terminée, il renvoie le résultat de l'opération immédiatement sans suspension de la méthode englobante .
L'opérateur await ne bloque pas le thread qui évalue la méthode async. Lorsque l'opérateur await suspend la méthode async englobante, le contrôle retourne à l'appelant de la méthode.
Voici donc ce qui se passe. Dans votre premier exemple, vous démarrez chaque tâche et attendez qu'elles se terminent consécutivement. C'est-à-dire que c'est comme demander à quelqu'un d'aller faire quelque chose et de terminer, avant de demander à la personne suivante de faire quelque chose, etc.
await TaskOne(); // start, do something and wait for it
await TaskTwo(); // start, do something and wait for it
await TaskThree(); // start, do something and wait for it
Votre deuxième exemple. Vous démarrez essentiellement les 3 tâches (chaudes), puis attendez qu'elles se terminent une à la fois. C'est-à-dire qu'ils s'exécutent simultanément et attendus en séquence.
C'est-à-dire que vous dites à 3 amis, allez faire des choses, puis attendez que le premier revienne, puis le deuxième puis le troisième. C'est beaucoup plus efficace ... Pas d'amis embêtants qui traînent jusqu'à ce que le précédent revienne.
Même si la deuxième tâche se termine avant la première , vous attendez toujours la première tâche avant de regarder l' état terminé de la deuxième tâche, etc.
var taskOne = TaskOne(); // start, do something
var taskTwo = TaskTwo(); // start, do something
var taskThree = TaskThree(); // start, do something
// all 3 tasks are started
await taskOne; // wait for it
await taskTwo; // wait for it
await taskThree; // wait for it
Ou de la même manière, vous pouvez utiliser Task.WhenAll
Crée une tâche qui se terminera lorsque toutes les tâches fournies seront terminées.
var taskOne = TaskOne(); // start, do something
var taskTwo = TaskTwo(); // start, do something
var taskThree = TaskThree(); // start, do something
// wait for them all to finish!
await Task.WhenAll(taskOne, taskTwo, taskThree);