#c_sharp #async_await #async_programming
На данный вопрос уже ответили: Нужен async/await или не нужен? 5 ответов Прочитал много литературы но пока никак не могу понять как работает 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 Объясните мне пожалуйста почему? Совсем не понимаю
Ответы
Ответ 1
Смотрите. Сами по себе 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(); }Ответ 2
У тебя ResultOfCycle() должна вернуть Task, но возвращает sum, которая является просто int. В самом методе нету второго потока, в котором он бы выполнялся. Await пишется перед объектом Task или методом, возвращающим объект Task. Пример для Task(вызов функции, которая ничего не возвращает): static void Main(string[] args) { My(); for (int i = 1; i <= 10; i++) { Thread.Sleep(1000); Console.WriteLine($"* {i*1000}"); } Console.ReadLine(); } static async void My() { await GetMessage(3000); } static Task GetMessage(int time) { return Task.Run(() => { Thread.Sleep(time); Console.WriteLine($"zxzxz {time.ToString()}"); }); } Пример для Task (вызов функции, которая возвращает объект типа T или в нашем случае string): static void Main(string[] args) { My(); for (int i = 1; i < 10; i++) { Thread.Sleep(1000); Console.WriteLine($"* {i*1000}"); } Console.ReadLine(); } static async void My() { string message = await GetMessage(3000); Console.WriteLine(message); } static Task GetMessage(int time) { return Task.Run(() => { Thread.Sleep(time); return $"zxzxz {time.ToString()}"; }); }
Комментариев нет:
Отправить комментарий