asynchrones Warten auf Verhalten [Duplikat]
Ich habe mit asynchronem Warten experimentiert und bin auf dieses ziemlich seltsame Verhalten gestoßen, zumindest für mich.
Ich habe drei Methoden erstellt, die lang laufende Aufgaben simulieren.
Betrachten Sie die beiden Handler
für das Klicken auf Schaltflächen: Für button1_click betrug die verstrichene Zeit etwa 6000 ms, während button2_click etwa 3000 ms betrug.
Ich bin nicht in der Lage, meinen Kopf darum zu wickeln, warum dies passiert ist, dh 6000 ms gegen 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();
}
Antworten
In diesem Fall:
await TaskOne();
await TaskTwo();
await TaskThree();
TaskTwo () kann erst gestartet werden, wenn TaskOne () abgeschlossen ist, da Sie darauf warten. Ebenso kann TaskThree () erst nach Abschluss von TaskTwo () gestartet werden.
Im nächsten:
var taskOne = TaskOne();
var taskTwo = TaskTwo();
var taskThree = TaskThree();
await taskOne;
await taskTwo;
await taskThree;
Sie starten alle drei Aufgaben gleichzeitig und warten dann auf sie. Deshalb dauert es nur so lange wie die am längsten laufende Aufgabe. Sie wären überrascht, wie viele Leute dies nicht verstehen, wenn sie auf Async warten. Wenn die Aufgaben nicht voneinander abhängig sind, ist dies der richtige Weg.
Zusammenfassung
Der Take-Home-Punkt hier (und es ist ansonsten ein sehr häufiges Missverständnis) ist, dass await
eigentlich wirklich "warten" bedeutet .
Warten auf Operator (C # -Referenz)
Hervorhebung von mir
Der Operator await unterbricht die Auswertung der einschließenden asynchronen Methode, bis die durch seinen Operanden dargestellte asynchrone Operation abgeschlossen ist . Wenn die asynchrone Operation abgeschlossen ist, gibt der Operator await gegebenenfalls das Ergebnis der Operation zurück.
Wenn der Warteoperator auf den Operanden angewendet wird, der eine bereits abgeschlossene Operation darstellt, gibt er das Ergebnis der Operation sofort zurück, ohne die Einschlussmethode auszusetzen .
Der Operator await blockiert nicht den Thread, der die asynchrone Methode auswertet. Wenn der Operator await die einschließende asynchrone Methode unterbricht, kehrt das Steuerelement zum Aufrufer der Methode zurück.
Also hier ist was passiert. In Ihrem ersten Beispiel starten Sie jede Aufgabe und warten darauf, dass sie nacheinander abgeschlossen werden. Das heißt, es ist, als würde man jemanden bitten, etwas zu tun und zu beenden, bevor er die nächste Person bittet, etwas zu tun usw.
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
Dein zweites Beispiel. Sie starten im Wesentlichen die 3 Aufgaben (heiß) und warten dann darauf, dass sie jeweils einzeln erledigt werden. Das heißt, sie laufen gleichzeitig und werden nacheinander erwartet.
Dh du sagst zu 3 Freunden, mach Sachen und warte darauf, dass der erste zurückkommt, dann der zweite und dann der dritte. Es ist viel effizienter ... Keine nervigen Freunde, die herumhängen, bis der vorherige zurückkommt.
Selbst wenn die zweite Aufgabe vor der ersten abgeschlossen ist , warten Sie tatsächlich auf die erste Aufgabe, bevor Sie den abgeschlossenen Status der zweiten Aufgabe usw. anzeigen .
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
Oder ähnlich könnten Sie verwenden Task.WhenAll
Erstellt eine Aufgabe, die abgeschlossen wird, wenn alle bereitgestellten Aufgaben abgeschlossen sind.
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);