async await Behavior [дубликат]

Nov 19 2020

Я экспериментировал с async-await и столкнулся с этим довольно странным поведением, по крайней мере, для меня.
Я создал три метода, имитирующих длительные задачи.
Рассмотрим два обработчика нажатия кнопки:
для button1_click истекшее время составило около 6000 мс, а для button2_click - около 3000 мс.
Я не могу понять, почему это произошло, то есть 6000 мс против 3000 мс.

    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();
    }

Ответы

7 dogyear Nov 20 2020 at 02:57

В этом случае:

 await TaskOne();
 await TaskTwo();
 await TaskThree();

TaskTwo () не может запуститься, пока TaskOne () не завершится, потому что вы его ждете. Точно так же TaskThree () не может запускаться, пока TaskTwo () не завершится из-за ожидания.

В следующий:

var taskOne = TaskOne();
var taskTwo = TaskTwo();
var taskThree = TaskThree();

await taskOne;
await taskTwo;
await taskThree;

Вы запускаете все три задачи одновременно и ждете их. Вот почему это занимает ровно столько времени, сколько выполняется самая долгая задача. Вы будете удивлены, узнав, как много людей не понимают этого об async await. Если задачи не зависят друг от друга, тогда это правильный путь.

7 00110001 Nov 20 2020 at 03:00

Резюме

Главный вывод здесь (а в противном случае это очень распространенное заблуждение) заключается в том, что на awaitсамом деле действительно означает «ждать» .


оператор ожидания (справочник по C #)

Акцент мой

Оператор await приостанавливает оценку включающего асинхронного метода до завершения асинхронной операции, представленной его операндом . Когда асинхронная операция завершается, оператор await возвращает результат операции, если таковой имеется.

Когда оператор ожидания применяется к операнду, который представляет уже завершенную операцию, он немедленно возвращает результат операции без приостановки включающего метода .

Оператор ожидания не блокирует поток, который оценивает метод async. Когда оператор await приостанавливает включающий асинхронный метод, элемент управления возвращается вызывающей стороне метода.


Итак, вот что происходит. В вашем первом примере вы запускаете каждую задачу и ожидаете ее последовательного завершения. То есть это как попросить кого-то пойти и что-то сделать и закончить, прежде чем просить следующего человека что-то сделать и т. Д.

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

Ваш второй пример. По сути, вы запускаете 3 задачи (горячие), а затем ждете, пока они закончатся по одному. То есть они выполняются одновременно и ожидают по очереди.

То есть вы говорите трем друзьям: иди делайте что-нибудь, затем ждете, пока вернется первый, затем второй, затем третий. Это намного эффективнее ... Никаких надоедливых друзей, слоняющихся до тех пор, пока не вернется предыдущий.

Даже если вторая задача завершается перед первым , вы все еще в силе в ожидании первой задачи , прежде чем смотреть на завершенном состоянии на второй задаче и т.д.

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

Или аналогично вы могли бы использовать Task.WhenAll

Создает задачу, которая будет завершена, когда будут выполнены все поставленные задачи.

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);