Другие ответы частично верны, но я хотел написать свой собственный ответ, чтобы что-то прояснить.
Задачи не запускаются параллельно.
Строгоговоря «параллельно» означает, что две строки кода оцениваются одновременно. Этого здесь не происходит.
Каждый асинхронный метод запускается синхронно. Магия происходит, когда await
действует на неполный Task
(например, когда запускается запрос ввода-вывода). В этот момент метод возвращает свой собственный неполный Task
, и продолжение метода запланировано на более позднее время.
Когда вы сделаете это:
var t1 = Task1();
var t2 = Task2();
var t3 = Task3();
Вот что происходит:
Task1
начинает выполняться. - При первом
await
, Task1
возвращает Task
. Task2
начинает выполнение. - При первом
await
, Task2
возвращает Task
. Task3
начинает выполнение. - При первом
await
, Task3
возвращает Task
.
Итак, что делает этот запуск быстрее, так как ваш код использует время, пока Task1
ожидает ответа для запуска запроса в Task2
.
продолжение этих задач может выполняться параллельно в зависимости от обстоятельств.
В ситуации, когда существует контекст синхронизации(как ASP.NET), продолжения должны возвращаться в один и тот же контекст, и ничто не будет работать параллельно. Это означает, что выполнение Task1
не будет продолжаться до тех пор, пока ничего не будет запущено в этом контексте. В вашем коде это происходит по адресу:
m1 = await t1;
Только после этой строки текущий контекст освобождается, в этом контексте может выполняться продолжение Task1
, а когда это будет сделано, все после await t1
добавляется в список «дел» для завершения.
Если вы работаете в ситуации, когда отсутствует контекст синхронизации (например, ASP.NET Core ), или вы указываете .ConfigureAwait(false)
чтобы сообщить, что вам не нужно возвращаться в тот же контекст, продолжения задач будут выполняться в потоках ThreadPool. Это означает, что продолжения всех трех задач могут выполняться параллельно в разных потоках. Если это произойдет, то к тому времени, когда вы нажмете await t1
, это уже может быть сделано.
Если вы сделаете это:
var m1 = await Task1();
var m2 = await Task2();
var m3 = await Task3();
Вы ждете, пока не закончится Task1
полностью, прежде чем даже начать Task2
. Это зависит от вашего приложения. Например, в ASP.NET он освобождает поток, который будет использоваться другим не связанным запросом. В настольном приложении он может освободить поток пользовательского интерфейса для ответа на ввод пользователя.