Непонятно с async-await в C# - 2
Удалите эту тему, пожалуйста Непонятно с async-await в C# - Программирование (germany.ru)
Вот задачка - что выведет код? Можно без временных меток (их для удобства просто добавил) - просто в какой последовательности сообщения будут?
class Program { static DateTime _startTime; static async Task Main(string[] args) { _startTime = DateTime.Now; var wait1 = Wait1(); var wait2 = Wait2(); var wait3 = Wait3(); await wait1; Console.WriteLine("after wait1"); await wait2; Console.WriteLine("after wait2"); await wait3; Console.WriteLine("after wait3"); } static async Task Wait1() { await Task.Delay(3000); Console.WriteLine($"wait1 is ready {(DateTime.Now - _startTime).Seconds}"); } static async Task Wait2() { await Task.Delay(2000); Console.WriteLine($"wait2 is ready {(DateTime.Now - _startTime).Seconds}"); } static async Task Wait3() { await Task.Delay(1000); Console.WriteLine($"wait3 is ready {(DateTime.Now - _startTime).Seconds}"); } }
У меня выводит
wait3 is ready 1
wait2 is ready 2
wait1 is ready 3
after wait1
after wait2
after wait3
и я не понимаю, почему. Я думал, что сообщения должны выводиться в той же последовательности, в которой заканчиваются выполняться таски. Т.е.
wait3 is ready 1
after wait3
wait2 is ready 2
after wait2
wait1 is ready 3
after wait1
Варинаты с разным временем ожидания
--------------------
1000
3000
2000
wait1 is ready 1
after wait1
wait3 is ready 2
wait2 is ready 3
after wait2
after wait3
------------------
3000
1000
2000
wait2 is ready 1
wait3 is ready 2
wait1 is ready 3
after wait1
after wait2
after wait3
-------------------
2000
3000
1000
wait3 is ready 1
wait1 is ready 2
after wait1
wait2 is ready 3
after wait2
after wait3
---------------
2000
1000
3000
wait2 is ready 1
wait1 is ready 2
after wait1
after wait2
wait3 is ready 3
after wait3
Когда бы ни закончились ожидаемые таски, код между ними всегда выполняется в том порядке, как написан
after wait1
after wait2
after wait3
Причём первая строка выполняется лишь после того, как закончится таска, ожидаемая перед этой первой строкой, независимо от того, закончились ли последующие таски.
Я тут вообще логики не вижу.
По сути, писать асинхронный код с ожиданием задач в таком виде мало смысла. Если я хочу, чтобы после каждой таски выполнялся свой код, и именно тогда, когда эта таска закончится, то нельзя писать этот код сразу после таски. Иначе этот код выполнится лишь тогда, когда все остальные таски и код выше выполнится.
Т. е. либо надо обязательно использовать ContinueWith, либо WhenAll, WhenAny и т.п. вспомогательные методы. Тот поток выполнения, что в вышеприведённом примере - нихрена не логичен.
https://docs.microsoft.com/en-us/dotnet/csharp/language-re...
The await
operator suspends evaluation of the enclosing async method until the asynchronous operation represented by its operand completes. When the asynchronous operation completes, the await
operator returns the result of the operation, if any. When the await
operator is applied to the operand that represents an already completed operation, it returns the result of the operation immediately without suspension of the enclosing method. The await
operator doesn't block the thread that evaluates the async method. When the await
operator suspends the enclosing async method, the control returns to the caller of the method.
Я тут вообще логики не вижу.
Не нужно просто вызывать асинхронные методы синхронно.
Поиграйтесь лучше с этим кодом.
class Program { private static DateTime _startTime; static async Task Main(string[] args) { _startTime = DateTime.Now; await Wait1(); Console.WriteLine("after wait1"); await Wait2(); Console.WriteLine("after wait2"); await Wait3(); Console.WriteLine("after wait3"); } private static async Task Wait1() { await Task.Delay(3000); Console.WriteLine($"wait1 is ready {(DateTime.Now - _startTime).Seconds}"); } private static async Task Wait2() { await Task.Delay(2000); Console.WriteLine($"wait2 is ready {(DateTime.Now - _startTime).Seconds}"); } private static async Task Wait3() { await Task.Delay(1000); Console.WriteLine($"wait3 is ready {(DateTime.Now - _startTime).Seconds}"); } }
код, который должен выполниться немедленно после завершения этой Task
То есть, независимо от других задач? -> https://docs.microsoft.com/en-us/dotnet/api/system.threadi...
код, который должен выполниться немедленно после завершения этой TaskТо есть, независимо от других задач? -> https://docs.microsoft.com/en-us/dotnet/api/system.threadi...
Да-да, я так и подумал - нужно использовать вспомогательные методы.
Не нужно просто вызывать асинхронные методы синхронно.
Поиграйтесь лучше с этим кодом.
Ну тут вообще всё последовательно. У меня хотя бы таски запускаются параллельно, но ожидаются последовательно - т.е. общее время выполнения всех тасков не больше времени для самой долгой таски. А у вас всё запусается и ожидается последовательно, поэтому общее время выполнения всех тасков всегда суммируется.
А какая стоит задача то?
С этим кодом может быть примерно такое извращение для паралельного исполнения
private static void RunParallel() { Task[] tasks = new Task[3]; tasks[0] = Task.Run(async () => { await Wait1(); }); tasks[1] = Task.Run(async () => { await Wait1(); }); tasks[2] = Task.Run(async () => { await Wait1(); }); Task.WaitAll(tasks); }
А так хотя бы сюды нужно глянуть
https://devblogs.microsoft.com/pfxteam/await-synchronizationcontext-and-console-apps/
А какая стоит задача то?
А так хотя бы сюды нужно глянутьhttps://devblogs.microsoft.com/pfxteam/await-synchronizati...
Я даже эту штуку пару раз проштудировал. А толку? Если не юзаешь постоянно, то вылетает из головы.
А так я играюсь с кодом. Я к этому async await уже раза три подходил, и каждый раз какой-то затык. Там куча подходов и паттернов применяется, и их местами смешивают. Даётся много свободы, но при этом правильных подходов не так много. Зато есть огромное число возможностей сделать неправильно. В этом и сложность. В этом отличие от коллбэчного подхода, где делается только вот так и никак иначе - не ошибёшься.
В МСДН написано, что "асинхронный" подход сделан, чтобы упростить многопоточное программирование по сравнению с "коллбэчным", но по факту они только больше всё запутали - без большого опыта и знания всех подводных камней легко выстрелить себе в ногу.
Вообще, у меня ещё отсюда тянется c# - How to close modal window after a Task completes? - Stack Overflow
Вроде, решения подходящие мне привели, но я так и не получил понимания, как самому до этого дойти. Тасуя туда-сюда метод закрытия окна, и не понимаю, почему он не срабатывает в определённых случаях, хотя вроде должен.
Вот из этого примера видно, что данный способ отображения прогресса и автозакрытия окна можно написать небольшим числом правильных способов и огромным числом неправильных. И нет одного шаблона, как в коллбэчном подходе, где указал методы для самой задачи, для задачи по завершению операции, и для задачи при возникновении ошибки - и так всегда однообразно поступаешь.
По первому видео возник вопрос. Вот картинка оттуда
Некий внешний код вызывает эту функцию, выполнение функции продолжается до await, если ожидаемая при await операция не завершена, выполнение возвращается в код, вызвавший показанную функцию. Далее выполнение продолжается во внешнем коде, но когда операция при await завершается, выполнение во внешнем коде блокируется и продолжается в данной функции после await?
Если да, то получается, что каждое завершение ожидания задачи в вызванной асинхронной функции прерывает вызвавший эту функцию код? Напишу много await - вызывающий код будет столько же раз прерван (если ожидаемые задачи не выполнены к коменту ожидания)?