Недавно я читал некоторый код, который использует много асинхронных методов, но иногда их нужно выполнять синхронно. В коде есть:
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 в двух словах