Недавно я читал некоторый код, который использует много асинхронных методов, но иногда их нужно выполнять синхронно. В коде есть:
Foo foo = GetFooAsync(...).GetAwaiter().GetResult();
Это то же самое, что
Foo foo = GetFooAsync(...).Result;
?
Недавно я читал некоторый код, который использует много асинхронных методов, но иногда их нужно выполнять синхронно. В коде есть:
Foo foo = GetFooAsync(...).GetAwaiter().GetResult();
Это то же самое, что
Foo foo = GetFooAsync(...).Result;
?
В значительной степени. Одна маленькая разница: если Task терпит неудачу, GetResult() просто выбросит исключение, вызванное напрямую, а Task.Result выкинет AggregateException. Однако, какой смысл использовать любой из них, когда он async? Опция 100x лучше использовать await.
Кроме того, вы не должны использовать GetResult(). Это предназначалось только для использования компилятором, а не для вас. Но если вы не хотите раздражать AggregateException, используйте его.
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
https://github.com/aspnet/Security/issues/59
"Последнее замечание: вам следует избегать использования
Task.ResultиTask.Waitкак насколько это возможно, поскольку они всегда инкапсулируют внутреннее исключение вAggregateExceptionи замените сообщение общим (одно или возникло больше ошибок), что усложняет отладку. Даже если синхронная версия не должна использоваться так часто, вы должны сильно вместо этого используйтеTask.GetAwaiter().GetResult().
Другое отличие заключается в том, что функция async возвращает только Task вместо Task<T>, тогда вы не можете использовать
GetFooAsync(...).Result;
В то время как
GetFooAsync(...).GetAwaiter().GetResult();
все еще работает.
Я знаю, что пример кода в вопросе относится к случаю Task<T>, однако вопрос задается в целом.
Как уже упоминалось, если вы можете использовать await. Если вам нужно запустить код синхронно, как вы упомянули .GetAwaiter().GetResult(), .Result или .Wait() - это риск для взаимоблокировок, как говорили многие в комментариях/ответах. Так как большинство из нас любят oneliners, вы можете использовать их для .Net 4.5<
Получение значения с помощью асинхронного метода:
var result = Task.Run(() => asyncGetValue()).Result;
Синхронный вызов асинхронного метода
Task.Run(() => asyncMethod()).Wait();
Из-за использования Task.Run проблем тупика не возникнет.
Источник:
В случае сбоя задачи исключение выдается повторно, когда код продолжения вызывает awaiter.GetResult(). Вместо вызова GetResult мы могли бы просто получить доступ к свойству Result задачи. Преимущество вызова GetResult заключается в том, что в случае сбоя задачи исключение генерируется напрямую, без включения в AggregateException, что позволяет создавать более простые и чистые блоки перехвата.
Для неуниверсальных задач GetResult() имеет возвращаемое значение void. Его полезная функция - исключать исключения.
источник: С# 7.0 в двух словах