#c_sharp #async_await
Обнаружил странное. Если создаю Task таким образом var task = new Task(async action); task.Start(); то при вызове любого асинхронного метода (await Task.Delay(), await new WebClient().DownloadStringAsync(), etc.) игнорируется оператор await и код сразу же возвращается к основному потоку. Если же Task конструирую рекомендуемым во всех статьях способом (через Task.Run(async action)), то всё работает, как ожидается. Пример кода, иллюстрирующий проблему: class Program { static async Task Main() { // way1 - it's working var goodTask = Task.Run(async () => await ProblemMethod("goodTask")); await goodTask; Console.WriteLine($"{DateTime.Now:HH:mm:ss} :: {nameof(goodTask)} is finished."); // way2 - it fails without any exceptions on line with GetRequestStreamAsync var poorTask = new Task(async () => await ProblemMethod("poorTask")); poorTask.Start(); await poorTask; Console.WriteLine($"{DateTime.Now:HH:mm:ss} :: {nameof(poorTask)} is finished."); Console.ReadLine(); } static async Task ProblemMethod(string taskName) { Console.WriteLine($"{taskName} :: {DateTime.Now:HH:mm:ss} :: It's a first line."); await Task.Delay(2000); Console.WriteLine($"{taskName} :: {DateTime.Now:HH:mm:ss} :: It's a last line."); } } В консоль выводится: goodTask :: 18:52:50 :: It's a first line. goodTask :: 18:52:52 :: It's a last line. 18:52:52 :: goodTask is finished. poorTask :: 18:52:52 :: It's a first line. 18:52:52 :: poorTask is finished. poorTask :: 18:52:54 :: It's a last line. Почему такое странное поведение во втором случае ("it's a last line" выводится уже после возвращения в основной поток? Оператор await ведь присутствует... P.S. Да, я в курсе, что рекомендуемый способ работы с тасками в TAP - это сразу же запускать их при конструировании (через Task.Run или Task.Factory.StartNew), но как следует поступать, если необходимо отделять конструирование тасков от команды на их исполнение, т.е., если надо реализовать их отложенное выполнение?
Ответы
Ответ 1
Проблема в том, что конструктор new Task() принимает только Action и Actionс разными комбинациями токена отмены, стейта и т.д, но нет перегрузки, принимающей Func или Func чтобы можно было дождаться переданной асинхронной лямбды. В то время как Task.Run имеет такую перегрузку. Таким образом - у вас возникает классическая проблема ожидания async void. Если мы сделаем var result = await poorTask; то фактически мы получим void и ошибку компиляции. Для того чтобы дождаться внутренней задачи, обычно нужно сделать Unwrap или добавить еще один await, но в данном случае мы получаем void и дождаться внутренней задачи не можем. Как описал в комментарии @PetSerAl нужно использовать generic версию Task чтобы иметь возможность дождаться внутренней задачи.
Комментариев нет:
Отправить комментарий