Различное поведение 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сек
Может ли кто-нибудь объяснить, почему? Методы почти одинаковы. В чем разница?
Ответы
Ответ 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
просто гарантирует, что он должен закончить, прежде чем продолжить.