Разница между передачей обычной и асинхронной анонимной функции в Task.Запуск для асинхронной работы

#c# #asynchronous #async-await #anonymous-function

#c# #асинхронный #async-ожидание #анонимная функция

Вопрос:

В чем разница между

 ResultType result = await Task.Run(() => GetResultAsync())
  

и

 ResultType result = await Task.Run(async() => await GetResultAsync())
  

Я бы предположил, что первая сработает и забудет GetResultAsync() , поскольку ее не ожидают, но тогда как она получает результат? Я удивлен, что возвращаемый тип первого Task.Run является Task<ResultType> , а не Task<Task<ResultType>> нет.

Комментарии:

1. В чем разница между списком дел с надписью «сделай сэндвич» и списком дел с надписью «составь список дел с надписью «сделай сэндвич», а затем делай то, что есть в этом списке»? Если вы можете объяснить мне, в чем разница между этими двумя вещами, я могу объяснить вам, в чем разница между этими двумя фрагментами кода.

2. Я не понимаю, откуда берутся ваши предположения; можете ли вы лучше объяснить, почему вы удивлены тем, что вас удивляет? Task.Run Метод набран как принимающий Func<Task<T>> , так почему вы удивлены, что он правильно обрабатывает этот случай?

3. Привет, Эрик, спасибо за ответ. Позвольте мне лучше объяснить мои предположения / трудности в понимании…

4. …Я думал, что возвращаемый тип GetResultAsync — это Task<ResultType> и задача. Запуск вернул бы задачу возвращаемого типа, который был бы Task<Задача<Тип результата>> . В последнем примере в моем вопросе типом возвращаемого await GetResultAsync является ResultType и Task. Run возвращает задачу с этим типом возвращаемого значения. Я не понял задачу. Run ведет себя по-разному для функций задач по сравнению с функциями других типов .

5. Все, что было сказано: более уместным вопросом, который вы должны задать, является «зачем я вообще все это делаю?» Просто напишите свой код как ResultType result = await GetResultAsync(); и вообще не возитесь с Task.Run ! Это уже асинхронно; нет необходимости делать это более асинхронным.

Ответ №1:

Оба делают то же самое с точки зрения результата. В обоих случаях используется перегрузка Task.Run<TResult>(Func<Task<TResult>> function) , которая внутренне разворачивает задачу — вот почему результат Task<ResultType> . Разница между ними равнозначна разнице между

     static Task<T> Compute()
    {
        return GetAsyncResult();
    }
  

и

     static async Task<T> Compute()
    {
        return await GetAsyncResult();
    }
  

В первом случае обещание просто передается обратно вызывающей стороне, в то время как во втором конечный автомат создается компилятором на основе Compute метода.

Ответ №2:

В первой строке ‘Задача.Запуск’ при запуске немедленно возвращает результат ‘GetResultAsync’. Однако этот результат является задачей, которую можно ожидать (вашим ‘await’). Итак, вы на самом деле «ожидаете» метод GetResultAsync.

Во второй строке вы запускаете задачу, которая не возвращается немедленно, а ожидает завершения ‘GetResultAsync’ (вызывается асинхронно). Возвращаемый тип вашей задачи — ‘ResultType’. Таким образом, вы «ожидаете» свою собственную задачу, которая, в свою очередь, вернется только после ожидания метода ‘GetResultAsync’.

В конце концов, они оба достигают одного и того же результата, но немного по-разному.