#c# #async-await #task
Вопрос:
У меня есть такой метод :
public static Task TaskMethod ()
{
Task.Delay(30000);
return Task.Run(() => Console.WriteLine("From Task") );
}
Если я назову это так:
public static async Task Main()
{
Console.WriteLine("before calling the task");
TaskMethod();
Console.WriteLine("after calling the task");
Console.ReadLine();
}
У меня есть этот вывод:
перед вызовом задачи
после вызова задачи
из задачи
Но если я назову это:
public static async Task Main()
{
Console.WriteLine("before calling the task");
await TaskMethod();
Console.WriteLine("after calling the task");
Console.ReadLine();
}
У меня есть такой результат:
перед вызовом задачи
из задачи
после вызова задачи
Является ли такое поведение нормальным? Почему мне разрешено ждать выполнения задачи без использования асинхронности?
Чего мне не хватает?
Комментарии:
1. Обратите внимание, что
Task.Delay
в вашем коде ничего не делается.2. Вы
await
Task
а, и это то, чтоTaskMethod
возвращается.3. Ваш фактический вопрос:
async
является ли модификатором, который позволяетawait
использовать в рамках метода, к которому применяется модификатор. ЭтоTask
то, что позволяет вам ожидать метод, независимо от того, имеетasync
ли он модификатор. Надеюсь, кто-нибудь сможет объяснить это более красноречиво, чем я.4. Все
await
потребности-это бегTask
или что-то другое, чего можно ожидать. Он не знает и не заботится о том, как вы создали такую вещь.5. Я не думаю, что вы находитесь на стадии постановки вопроса, похоже, вам просто нужно больше читать. Любой ответ здесь не заполнит пробелы, чтобы вы поняли это до нужного вам уровня
Ответ №1:
Я постараюсь объяснить.
Случай: вы не ждете возвращаемого значения, тогда оно выполнит строку и перейдет к выполнению следующей.
Console.WriteLine("before calling the task"); // execute 1
TaskMethod(); // execute 2
Console.WriteLine("after calling the task"); // execute 3
Запутанный вывод, который вы получаете, заключается в том, что вашей машине требуется меньше времени для выполнения 3-й команды, чем для выполнения 2-й (создание задачи, ее запуск, печать в консоль). 2 — й вызов выполняется параллельно 3-му. Это также связано с тем, что Task.Delay
звонок не ждет, потому что вы await
этого не делаете, и там действует тот же принцип, что и здесь.
Чтобы дать вам небольшое примерное доказательство, вот код, который вводит небольшую синхронную задержку между 2-м и 3-м вызовами:
public static async Task Main()
{
Console.WriteLine("before calling the task");
TaskMethod();
Thread.Sleep(50);
Console.WriteLine("after calling the task");
//Console.ReadLine();
}
Это дает начало 2-го вызова немного больше времени для выполнения до выполнения 3-го шага. И вы получите результат:
перед вызовом задачи
из задачи
после вызова задачи
Теперь перейдем к тому факту, что метод Main
фактически выполняется синхронно в основном потоке. На этом снимке экрана из LINQPad показано сообщение, которое также будет отображаться в Visual Studio:
Это говорит вам, что основной метод заблокирует основной поток до тех пор, пока он не будет завершен. Потому что в этом нет никакого ожидания.
В то TaskMethod
время как внутри Main
будет выполняться асинхронно и параллельно со следующими командами, которые выполняются в основном потоке! Тот, кто быстрее, сможет распечатать это сообщение.
Почему Task.Delay
вызов бесполезен без ожидания ?=!
Delay
Метод возвращает запущенную задачу, на самом деле совершенно такую же, как у вас TaskMethod
. Если вы не дождетесь этой задачи задержки, она будет выполняться параллельно. Таким образом, стирается весь эффект задержки. Вы действительно должны заметить, что ваша консоль печатает все 3 строки одновременно, между второй и третьей строками нет задержки!
Почему мне разрешено ждать выполнения задачи без использования асинхронности?
Потому что необходимость async
всегда относится к методу, внутри которого await
выполняется вызов, а не к ожидаемому методу! Это можно увидеть в том факте, что вы не можете просто добавить ожидание к Task.Delay
вызову, потому TaskMethod
что оно не объявлено как async
!
(Надеюсь) последнее РЕДАКТИРОВАНИЕ:
await
Оператору нужен Task
оператор, выполнения которого он может ожидать, и поскольку вы TaskMethod
возвращаете a Task
, оператор может быть применен. С его помощью вы можете делать приятные вещи, если возьмете на себя задачу возвращения. Затем вы можете решить, когда ждать окончания его выполнения!:
public static async Task Main()
{
Console.WriteLine("before calling the task");
Task taskToBeAwaitedMuchLater = TaskMethod();
Console.WriteLine("after calling the task");
await taskToBeAwaitedMuchLater;
Console.WriteLine("after awaiting the result of the task");
//Console.ReadLine();
}
Выход:
перед вызовом задачи
после вызова задачи
из задачи
после ожидания результата задачи
Комментарии:
1. «…это потому, что компилятору требуется меньше времени для выполнения 3-го…» разве это не должно быть время выполнения 😉
2. @BernoulliIT, вы правы, thx. изменил его на «ваша машина» 😉
3. «Эта задержка выполняется снова в другом потоке, и если вы ее не дождетесь, она будет выполняться параллельно». На самом деле для «выполнения» задачи не требуется поток
Task.Delay
. Если вы создадите 1 000 000Task.Delay
задач, вы не увидите 1 000 000 потоков в диспетчере задач Windows.4. Жизнь Монг Чжу слишком коротка, чтобы исправлять смысловые ошибки в сообщениях других людей. Я готов исправить орфографические ошибки, но не более того. 😃
5. @TheodorZoulias исправил это.