async await Behavior [duplikat]
Eksperymentowałem z async-awat i natknąłem się na to dość dziwne zachowanie, przynajmniej dla mnie.
Stworzyłem trzy metody symulujące długotrwałe zadania.
Rozważ dwie funkcje obsługi kliknięcia przycisku:
dla button1_click czas, który upłynął, wynosił około 6000 ms, a button2_click około 3000 ms.
Nie jestem w stanie wyjaśnić, dlaczego tak się stało, tj. 6000 ms vs 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();
}
Odpowiedzi
W tym przypadku:
await TaskOne();
await TaskTwo();
await TaskThree();
TaskTwo () nie może się uruchomić, dopóki TaskOne () nie zakończy się, ponieważ czekasz na to. Podobnie, TaskThree () nie może rozpocząć się, dopóki TaskTwo () nie zostanie zakończone z powodu oczekiwania.
W następnym:
var taskOne = TaskOne();
var taskTwo = TaskTwo();
var taskThree = TaskThree();
await taskOne;
await taskTwo;
await taskThree;
Rozpoczynasz wszystkie 3 zadania w tym samym czasie i czekasz na nie. Dlatego trwa to tylko tak długo, jak najdłużej działające zadanie. Zdziwiłbyś się, ilu ludzi nie rozumie tego o oczekiwaniu asynchronicznym. Jeśli zadania nie są od siebie zależne, to jest właściwy sposób.
Podsumowanie
Punktem wyjścia tutaj (a poza tym jest to bardzo powszechne nieporozumienie) jest to, że await
tak naprawdę oznacza „czekaj” .
await operator (odwołanie w C #)
Podkreśl moje
Operator await zawiesza szacowanie otaczającej metody asynchronicznej do momentu zakończenia operacji asynchronicznej reprezentowanej przez jej operand . Po zakończeniu operacji asynchronicznej operator await zwraca wynik operacji, jeśli taki istnieje.
Gdy operator await zostanie zastosowany do operandu, który reprezentuje już zakończoną operację, zwraca wynik operacji natychmiast bez zawieszania otaczającej metody .
Operator await nie blokuje wątku, który ocenia metodę asynchroniczną. Gdy operator await zawiesza otaczającą metodę asynchroniczną, formant wraca do obiektu wywołującego metodę.
Więc oto co się dzieje. W pierwszym przykładzie rozpoczynasz każde zadanie i czekasz, aż zakończą się po kolei. To znaczy, jakby poprosić kogoś, aby poszedł, zrobił coś i skończył, zanim poprosisz następną osobę o zrobienie czegoś itp
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
Twój drugi przykład. Zasadniczo rozpoczynasz 3 zadania (gorące), a następnie czekasz, aż zakończą się jedno na raz. To znaczy, że działają równolegle i czekają w kolejności.
To znaczy mówisz do 3 znajomych, idź zrób coś, potem czekaj, aż pierwszy wróci, potem drugi, potem trzeci. Jest o wiele bardziej wydajne ... Żadnych nieznośnych przyjaciół nie kręcących się, dopóki nie wróci poprzedni.
Nawet jeśli drugie zadanie finalizuje zanim pierwszy , wciąż jesteś w efekcie czeka na pierwsze zadanie przed patrząc na wypełnionym stanu w drugim zadaniu 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
Lub podobnie możesz użyć Task.WhenAll
Tworzy zadanie, które zostanie ukończone po zakończeniu wszystkich podanych zadań.
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);