Задача с/без синхронизации и ожидания

#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 000 Task.Delay задач, вы не увидите 1 000 000 потоков в диспетчере задач Windows.

4. Жизнь Монг Чжу слишком коротка, чтобы исправлять смысловые ошибки в сообщениях других людей. Я готов исправить орфографические ошибки, но не более того. 😃

5. @TheodorZoulias исправил это.