Как работают await async [дубликат]

1,00
р.



На этот вопрос уже даны ответы здесь:

Нужен async/await или не нужен?
(5 ответов)
Закрыт 7 лет назад.

Прочитал много литературы но пока никак не могу понять как работает await и async. Ну хоть убейте. Везде примеры с httpclient, но для меня они не понятны. Пытаюсь разобраться сам. Вот что я понял:
Как только наш код встречает await происходит возврат управления. После завершения ожидаемой операции метод восстанавливается. Точнее продолжает выполнение с того места, на котором остановился, когда столкнулся с await.
Хорошо, я написал пару строк кода(возможно просто что-то сделал не так)
async Task myMethod() { int sum = 0 await SomeCycleAsync() Console.WriteLine("выполнился цикл2") } async Task SomeCycleAsync() { var myTask = await ResultOfCycle()
Console.WriteLine("выполнился цикл1") } async Task < int > ResultOfCycle() { int sum = 0 for (int i = 0 i < 1000000000 i++) { sum += i } return sum }
private void Form1_Load(object sender, EventArgs e) { myMethod() }
В методе myMethod встречается слово await и, на сколько я понимаю, управление должна перейти обратно в form_load, верно? Во время выполнения метода SomeCycleAsync встречается await, т.е. по логике управление должно перейти к Console.WriteLine("выполнился цикл2") Но результат работы такой:
выполнился цикл1 выполнился цикл2
Объясните мне пожалуйста почему? Совсем не понимаю

Ответ
Смотрите.
Сами по себе async/await не включают таинственным образом многопоточность/асинхронность. Они лишь создают условия, при которых эту самую асинхронность легко реализовать.
На самом деле, когда вызывается async-метод, происходит следующее.
Начинает синхронно выполняться async-метод. Если этот метод заканчивается до первого await, результат доставляется синхронно, и из метода возвращается уже закончившийся, завершённый Task. Если в процессе выполнения встретился await, система проверяет, отработало ли уже задание, на которое вызывался await. Если это задание отработало, то подставляется его результат, и синхронное выполнение продолжается дальше. Если задание, на которое происходит await, ещё не отработало, в этот момент из метода возвращается незавершённый Task. В этой точке внешний код получает управление и продолжает выполняться. Например, этот код может записать Task в переменную и продолжать заниматься своими делами. Или он может выполнить await на полученный Task, и поскольку этот Task ещё не завершён, внешний код в этот момент аналогично отдаст управление ещё более внешнему коду, и. т. д. Когда Task, на который происходит await, завершится (произведя результат или исключение), код после await возобновит свою работу.

В вашем случае происходит следующее:
Вызывается Form1_Load. Выполняется строчка myMethod() . Этот код произведёт Task, который впоследствии будет просто проигнорирован. Начинает выполняться код метода myMethod. Выполняется синхронно int sum = 0 . Для выполнения следующей строчки для начала нужно выполнить метод SomeCycleAsync а затем await на результирующий Task. Начинается выполнение SomeCycleAsync. Для получения Task'а, по которому нужно делать await, запускается метод ResultOfCycle. Начинается выполнение ResultOfCycle(). Поскольку нигде в нём нету асинхронных вызовов, он выполняется полностью синхронно. Из метода возвращается завершённый Task. Управление возвращается в SomeCycleAsync. Выполняется await на Task, полученный в предыдущем пункте. Поскольку этот Task уже завершён, в переменную myTask просто записывается int-результат. Выполняется строчка Console.WriteLine("выполнился цикл1") . На этом выполнение метода SomeCycleAsync оканчивается. Поскольку в нём не было асинхронного ожидания, возвращается завершённый Task. Управление возвращается в метод myMethod(). Начинает выполняться await на полученный Task. Поскольку Task завершён, ничего не происходит, метод продолжает синхронно выполняться. Срабатывает строчка Console.WriteLine("выполнился цикл2") , метод заканчивается, возвращая завершённый Task. Управление возвращается в Form_Load. Полученный Task игнорируется, выполнение завершается.

Для ваших целей правильнее было бы явно запускать вычисления асинхронным образом. Например, так:
async Task myMethod() { await SomeCycleAsync() Console.WriteLine("выполнился цикл-2") }
async Task SomeCycleAsync() { Console.WriteLine("стартует цикл") // это запускает длинное вычисление на пуле потоков var result = await Task.Run(ResultOfCycle) Console.WriteLine("выполнился цикл, результат: " + result) }
int ResultOfCycle() { int sum = 0 for (int i = 0 i < 1000000000 i++) sum += i return sum }
private async void Form1_Load(object sender, EventArgs e) { await myMethod() }