Чи Task.Result такий же, як .GetAwaiter.GetResult ()?


328

Нещодавно я читав якийсь код, який використовує безліч методів асинхронізації, але іноді потрібно виконувати їх синхронно. Цей код:

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

Це те саме, що

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

8
З документів GetResult: "Цей тип та його члени призначені для використання компілятором." Інша людина не повинна ним користуватися.
витрачений

32
Це називається "синхронізація над асинхронією", і якщо ви не знаєте, як реалізується завдання, це може бути дуже поганою ідеєю. Він може негайно зайти в глухий кут у багатьох випадках (наприклад, async/ awaitметод у MVC)
Marc Gravell


14
У реальному світі у нас є конструктори, у нас немає «чекання» інтерфейсів, які нам потрібно реалізувати, і нам всюди надаються методи асинхронізації. Мені б із задоволенням скористатися чимось, що просто працює, не маючи питання, чому це "небезпечно", "не використовувати" або "уникати будь-якою ціною". Кожен раз, коли мені доводиться возитися з асинхронією, виявляється головний біль.
Ларрі

Відповіді:


173

Досить. Однак одна невелика різниця: якщо Taskне вдасться, GetResult()просто викине виняток, спричинений безпосередньо, тоді як Task.Resultвикине AggregateException. Однак який сенс використовувати будь-який із тих, коли він є async? На 100 разів кращим варіантом є використання await.

Крім того, ви не призначені для використання GetResult(). Це призначено для використання лише для компілятора, а не для вас. Але якщо ви не хочете дратівливого AggregateException, використовуйте його.


27
@JayBazuzi Якщо не, якщо ваш блок тестування модулів підтримує тести асинхронних одиниць, що, на мій погляд, це роблять новітні версії більшості фреймворків.
svick

15
@JayBazuzi: MSTest, xUnit і NUnit усі async Taskтести підтримки , і вже деякий час.
Стівен Клірі

18
натискання на 100x - це в 1000 разів гірше, якщо використовувати функцію очікування, якщо ви адаптуєте старий код, а для використання await потрібно переписати.
застряг


15
The 100x better option is to use await.Я ненавиджу такі твердження, якби я міг ляпати awaitперед цим, я би. Але, коли я намагаюся отримати асинхронний код для роботи з не-асинхронним кодом , як то , що часто трапляється зі мною багато в Xamarin, я в кінцевому підсумку того , щоб використовувати такі речі , як ContinueWithбагато для того , щоб зробити це не тупикову інтерфейс. Редагувати: Я знаю, що це давнє, але це не полегшує мої розчарування, шукаючи відповіді, які заявляють про це без альтернатив для ситуацій, коли ви не можете просто використовувати await.
Томас Ф.

147

Task.GetAwaiter().GetResult()є кращим Task.Waitі Task.Resultтому, що він поширює винятки, а не загортає їх у AggregateException. Однак усі три методи спричиняють можливі проблеми з голодуванням та голодуванням в пулі. Їх слід уникати на користь async/await.

Цитата нижче пояснює, чому Task.Waitта Task.Resultне просто містять поведінку щодо поширення винятків Task.GetAwaiter().GetResult()(через "дуже високу смугу сумісності").

Як я вже згадував раніше, у нас дуже високий рівень сумісності, і тому ми уникали порушень змін. Як така, Task.Waitзберігає свою первісну поведінку завжди обгортаючи. Однак ви можете опинитися в деяких складних ситуаціях, коли ви хочете, щоб поведінка була аналогічною синхронному блокуванню, використовуваному Task.Wait, але де ви хочете, щоб оригінальний виняток розповсюджувався розпакованим, а не містився в AggregateException. Для цього можна безпосередньо націлити на офіціанта Завдання. Коли ви пишете " await task;", компілятор переводить це у використання Task.GetAwaiter()методу, який повертає екземпляр, який має 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
Так що в основному Task.GetAwaiter().GetResult()рівнозначно await task. Я припускаю, що перший варіант використовується, коли метод не може бути позначений символом async(наприклад, конструктор). Це правильно? Якщо так, то він зіткнеться з головною відповіддю @ ItNotALie
OlegI

5
@OlegI: Task.GetAwaiter().GetResult()є більш еквівалентним Task.Waitі Task.Result(тим, що всі три будуть блокувати синхронно і матимуть потенціал для тупикових ситуацій), але Task.GetAwaiter().GetResult()має виняток поведінки розповсюдження очікуваного завдання.
Нітін Агарвал

Ви не можете уникнути тупикових ситуацій у цьому сценарії за допомогою (Завдання) .ConfigureAwait (false) .GetAwaiter (). GetResult (); ?
Даніель Лоренц

3
@DanielLorenz: Дивіться наступну цитату: "Використання ConfigureAwait (false) для уникнення тупикових ситуацій є небезпечною практикою. Вам потрібно буде використовувати ConfigureAwait (false) для кожного очікування в перехідному закритті всіх методів, викликаних блокуючим кодом, включаючи всі треті - та сторонній код. Використання ConfigureAwait (false) для уникнення тупикової ситуації в кращому випадку просто хак) ... краще рішення - "Не блокувати код асинхронізації". " - blog.stephencleary.com/2012/07/dont-block-on-async-code.html
Нітін Агарвал

4
Я не розумію. Task.Wait і Task.Result порушені дизайном? Чому вони не застаріли?
osexpert

69

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

"Останнє зауваження: вам слід уникати використання Task.Resultта Task.Waitнаскільки це можливо, оскільки вони завжди інкапсулюють внутрішній виняток у AggregateExceptionі замінюють повідомлення загальним (сталася одна чи більше помилок), що робить налагодження більш важким. Навіть якщо синхронна версія не повинна 'не буде використовуватися так часто, вам слід настійно розглянути можливість використання Task.GetAwaiter().GetResult()замість цього.'


20
Джерело, на яке посилається тут, - хтось цитує когось іншого, без посилання. Розглянемо контекст: я можу бачити безліч людей в сліпому режимі GetAwaiter (). GetResult () скрізь після цього, прочитавши це.
Jack Ukleja

2
Тож ми не повинні ним користуватися?
tofutim

11
Якщо два завдання закінчуються виключенням, ви втратите друге в цьому сценарії Task.WhenAll(task1, task2).GetAwaiter().GetResult();.
Монсеньйор


33

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

GetFooAsync(...).Result;

Тоді як

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

все ще працює.

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


1
Це не правда. Перевірте мою скрипку , яка використовує саме цю конструкцію: dotnetfiddle.net/B4ewH8
wojciech_rak

3
@wojciech_rak У коді ви використовуєте, Resultз GetIntAsync()яким повертається Task<int>не просто Task. Я пропоную вам ще раз прочитати мою відповідь.
Нурі Тасдемір

1
Ти маєш рацію, спершу я зрозумів, що ти відповідаєш, що ти не можеш GetFooAsync(...).Result всередині функції, яка повертається Task. Це тепер має сенс, оскільки в C # ( Task.Resultвластивість) немає недійсних властивостей , але ви, звичайно, можете викликати метод void.
wojciech_rak

22

Як вже було сказано, якщо ви можете використовувати await. Якщо вам потрібно запустити код синхронно, як ви згадуєте .GetAwaiter().GetResult(), .Resultабо .Wait()це ризик для тупикових ситуацій, як багато хто говорив у коментарях / відповідях. Оскільки більшості з нас подобаються oneliners, ви можете використовувати їх для.Net 4.5<

Отримання значень за допомогою методу асинхронізації:

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

Синхронно викликає метод асинхронізації

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

Жодних проблем із тупиком не виникне через використання Task.Run .

Джерело:

https://stackoverflow.com/a/32429753/3850405


1

Якщо завдання виходить з ладу, виняток повторно скидається, коли код продовження викликає awaiter.GetResult (). Замість того, щоб викликати GetResult, ми могли просто отримати доступ до властивості Result завдання. Перевага виклику GetResult полягає в тому, що якщо завдання виходить з ладу, виняток буде викинуто безпосередньо без загортання в AggregateException, що забезпечує більш прості та чисті блоки вилову.

Для неенергетичних завдань GetResult () має недійсне значення повернення. Тоді його корисна функція полягає виключно в тому, щоб повторно скинути винятки.

Джерело: c # 7.0 в горішці

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.