Является ли Task.Result тем же, что и .GetAwaiter.GetResult()?

Недавно я читал некоторый код, который использует много асинхронных методов, но иногда их нужно выполнять синхронно. В коде есть:

Foo foo = GetFooAsync(...).GetAwaiter().GetResult();

Это то же самое, что

Foo foo = GetFooAsync(...).Result;

?

Ответ 1

В значительной степени. Одна маленькая разница: если Task терпит неудачу, GetResult() просто выбросит исключение, вызванное напрямую, а Task.Result выкинет AggregateException. Однако, какой смысл использовать любой из них, когда он async? Опция 100x лучше использовать await.

Кроме того, вы не должны использовать GetResult(). Это предназначалось только для использования компилятором, а не для вас. Но если вы не хотите раздражать AggregateException, используйте его.

Ответ 2

Task.GetAwaiter().GetResult() предпочтительнее, чем Task.Wait и Task.Result поскольку он распространяет исключения, а не заключает их в AggregateException. Тем не менее, все три метода вызывают потенциальные проблемы с блокировкой и блокировкой пула потоков. Их следует избегать в пользу async/await.

Приведенная ниже цитата объясняет, почему Task.Wait и Task.Result не просто содержат поведение распространения Task.GetAwaiter().GetResult() (из-за "очень высокой панели совместимости").

Как я упоминал ранее, у нас очень высокий уровень совместимости, и поэтому мы избежали серьезных изменений. Таким образом, Task.Wait сохраняет свое первоначальное поведение всегда переноса. Однако вы можете столкнуться с некоторыми Task.Wait ситуациями, когда вам нужно поведение, подобное синхронной блокировке, используемой Task.Wait, но где вы хотите, чтобы исходное исключение распространялось развернутым, а не заключенным в AggregateException. Чтобы достичь этого, вы можете напрямую нацелить Задачи на ожидание. Когда вы пишете " await task; ", компилятор преобразует это в использование метода Task.GetAwaiter(), который возвращает экземпляр, имеющий метод GetResult(). При использовании в GetResult() Задаче GetResult() будет распространять исходное исключение (таким образом " await task; " получает свое поведение). Таким образом, вы можете использовать " task.GetAwaiter().GetResult() ", если хотите напрямую вызывать эту логику распространения.

https://blogs.msdn.microsoft.com/pfxteam/2011/09/28/task-exception-handling-in-net-4-5/

" GetResult " на самом деле означает "проверить задачу на наличие ошибок"

В общем, я стараюсь изо всех сил избегать синхронной блокировки асинхронной задачи. Однако есть несколько ситуаций, когда я нарушаю это правило. В этих редких случаях моим предпочтительным методом является GetAwaiter().GetResult() потому что он сохраняет исключения задач, а не заключает их в AggregateException.

http://blog.stephencleary.com/2014/12/a-tour-of-task-part-6-results.html

Ответ 3

https://github.com/aspnet/Security/issues/59

"Последнее замечание: вам следует избегать использования Task.Result и Task.Wait как насколько это возможно, поскольку они всегда инкапсулируют внутреннее исключение в AggregateException и замените сообщение общим (одно или возникло больше ошибок), что усложняет отладку. Даже если синхронная версия не должна использоваться так часто, вы должны сильно вместо этого используйте Task.GetAwaiter().GetResult().

Ответ 4

Другое отличие заключается в том, что функция async возвращает только Task вместо Task<T>, тогда вы не можете использовать

GetFooAsync(...).Result;

В то время как

GetFooAsync(...).GetAwaiter().GetResult();

все еще работает.

Я знаю, что пример кода в вопросе относится к случаю Task<T>, однако вопрос задается в целом.

Ответ 5

Как уже упоминалось, если вы можете использовать await. Если вам нужно запустить код синхронно, как вы упомянули .GetAwaiter().GetResult(), .Result или .Wait() - это риск для взаимоблокировок, как говорили многие в комментариях/ответах. Так как большинство из нас любят oneliners, вы можете использовать их для .Net 4.5<

Получение значения с помощью асинхронного метода:

var result = Task.Run(() => asyncGetValue()).Result;

Синхронный вызов асинхронного метода

Task.Run(() => asyncMethod()).Wait();

Из-за использования Task.Run проблем тупика не возникнет.

Источник:

fooobar.com/questions/23464/...

Ответ 6

В случае сбоя задачи исключение выдается повторно, когда код продолжения вызывает awaiter.GetResult(). Вместо вызова GetResult мы могли бы просто получить доступ к свойству Result задачи. Преимущество вызова GetResult заключается в том, что в случае сбоя задачи исключение генерируется напрямую, без включения в AggregateException, что позволяет создавать более простые и чистые блоки перехвата.

Для неуниверсальных задач GetResult() имеет возвращаемое значение void. Его полезная функция - исключать исключения.

источник: С# 7.0 в двух словах