Подтвердить что ты не робот

Различное поведение async/ждут почти теми же методами

Скажем, у меня есть два асинхронных метода

public async static Task RunAsync1()
{
    await Task.Delay(2000);
    await Task.Delay(2000);
}

и

public async static Task RunAsync2()
{
    var t1 = Task.Delay(2000);
    var t2 = Task.Delay(2000);

    await t1;
    await t2;
}

Затем я использую его как

public static void M()
{
    RunAsync1().GetAwaiter().GetResult();
    RunAsync2().GetAwaiter().GetResult();
}

В результате RunAsync1 будет работать 4сек, но RunAsync2 только 2сек
Может ли кто-нибудь объяснить, почему? Методы почти одинаковы. В чем разница?

4b9b3361

Ответ 1

Во втором методе одновременно запускаются две задачи. Они оба закончатся через 2 секунды (поскольку они работают параллельно). В первом методе вы сначала запускаете один метод (2 секунды), дождитесь его завершения, затем запустите второй (еще 2 секунды). Ключевым моментом здесь является Task.Delay(..), который запускается правильно, когда вы его вызываете, а не когда вы его ждете.

Чтобы уточнить, первый метод:

var t1 = Task.Delay(2000); // this task is running now
await t1; // returns 2 seconds later
var t2 = Task.Delay(2000); // this task is running now
await t2; // returns 2 more seconds later

Второй метод:

var t1 = Task.Delay(2000); 
var t2 = Task.Delay(2000); // both are running now

await t1; // returns in about 2 seconds
await t2; // returns almost immediately, because t2 is already running for 2 seconds

Ответ 2

Просто просмотрите свой код:

public async static Task RunAsync1()
{
    await Task.Delay(2000); // Start a delay task, and WAIT for it to finish
    await Task.Delay(2000); // Start a delay task, and WAIT for it to finish
}

Итак, второй await Task.Delay(2000); вызывается после завершения первого вызова (через 2 секунды).

Во втором методе

public async static Task RunAsync2()
{
    var t1 = Task.Delay(2000); // Start a task
    var t2 = Task.Delay(2000); // Start a task

    await t1; // Wait for task to finish
    await t2; // Wait for task to finish
}

Итак, задачи t1 и t2 выполняются одновременно.

Если вы измените его на

public async static Task RunAsync3()
{
    var t1 = Task.Delay(2000); // Start a task
    await t1; // Wait for task to finish

    var t2 = Task.Delay(2000); // Start a task
    await t2; // Wait for task to finish
}

вы получите те же результаты, что и в RunAsync1.

Ответ 3

В первом случае вы говорите

public async static Task RunAsync1()
{
    var t1 = Task.Delay(2000);
    await t1;
    var t2 = await Task.Delay(2000);
    await t2;
}

Что соответствует

  • 0:00 Создайте обратный вызов за 2 секунды 0:00
  • 0:00 Подождите, пока обратный вызов не вернется 0:02
  • 0:02 Создайте обратный вызов через 2 секунды 0:02
  • 0:02 Подождите, пока обратный вызов не вернется 0:04
  • 0:04 return;

Второй случай

public async static Task RunAsync2()
{
    var t1 = Task.Delay(2000);
    var t2 = Task.Delay(2000);

    await t1;
    await t2;
}
  • 0:00 Создание обратных вызовов за 2 секунды 0:00
  • 0:00 Создание обратных вызовов за 2 секунды 0:00
  • 0:00 Ожидание первого обратного вызова 0:02
  • 0:02 Ждите второго обратного вызова 0:02
  • 0:02 return

Другими словами, в первом вы выполняете последовательное асинхронное программирование, а второе - параллельное асинхронное программирование.

Ответ 4

Всякий раз, когда вы запускаете задачу. Это уже началось, когда вы его создали, а не при вызове await.

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