async await Behavior [duplikat]

Nov 19 2020

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

7 dogyear Nov 20 2020 at 02:57

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.

7 00110001 Nov 20 2020 at 03:00

Podsumowanie

Punktem wyjścia tutaj (a poza tym jest to bardzo powszechne nieporozumienie) jest to, że awaittak 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);