Чекайте на виконане завдання те саме, що task.Result?


117

Зараз я читаю " Конкурс у C # Cookbook " Стівена Клірі, і я помітив таку техніку:

var completedTask = await Task.WhenAny(downloadTask, timeoutTask);  
if (completedTask == timeoutTask)  
  return null;  
return await downloadTask;  

downloadTaskє закликом до httpclient.GetStringAsyncта timeoutTaskвиконує Task.Delay.

У випадку, якщо він не downloadTaskзакінчився , він уже завершений. Чому потрібно зробити секунду очікування замість повернення downloadTask.Result, враховуючи, що завдання вже виконано?


3
Тут відсутнє трохи контексту, і якщо люди не мають доступу до книги, вам потрібно буде включити її. Що таке downloadTaskі timeoutTask? Що вони роблять?
Майк Перренод

7
Тут я не бачу фактичної перевірки на успішне завершення. Завдання цілком може бути помилковим, і в такому випадку поведінка буде іншою ( AggregateExceptionз Resultпершим винятком через ExceptionDispatchInfoз await). Більш детально обговорюється в розділі «Завдання обробки виключень в .NET 4.5» Стівена Toub в: blogs.msdn.com/b/pfxteam/archive/2011/09/28 / ... )
Кирило Шльонський

ви повинні відповісти на це @KirillShlenskiy
Карстен

@MichaelPerrenoud Ви маєте рацію, дякую, що помітили, я відредагую питання.
julio.g

Відповіді:


160

Тут вже є кілька хороших відповідей / коментарів, але просто для того, щоб звучати ...

Є дві причини, чому я віддаю перевагу awaitнад Result(або Wait). Перший полягає в тому, що обробка помилок відрізняється; awaitне включає виняток у AggregateException. В ідеалі асинхронним кодом взагалі ніколи не слід мати справу AggregateException, якщо цього конкретно не хочеться .

Друга причина - трохи більш тонка. Як я описую в своєму блозі (і в книзі), Result/ Waitможе призвести до тупиків і може призвести до ще більш тонких тупиків при використанні asyncметоду . Отже, коли я читаю код і бачу Resultабо Wait, це негайний попереджувальний прапор. Result/ WaitЄ єдино правильним , якщо ви абсолютно впевнені в тому , що завдання вже виконана. Це не тільки важко помітити з першого погляду (в реальному коді), але і більш крихким для зміни коду.

Це не означає , що Result/ Waitне повинні ніколи бути використані. Я дотримуюся цих вказівок у власному коді:

  1. Асинхронний код у програмі може використовуватись лише await.
  2. Асинхронний код утиліти (у бібліотеці) може періодично використовуватись Result/ Waitякщо код дійсно вимагає цього. Таке використання, мабуть, має зауваження.
  3. Паралельний код завдання може використовувати Resultі Wait.

Зауважимо, що (1) на сьогоднішній день є загальним випадком, отже, моя тенденція використовувати awaitвсюди і трактувати інші випадки як винятки із загального правила.


Ми зіткнулися з тупиком, використовуючи "результат" замість "очікувати" в наших проектах. у заплутаній частині не виникає помилок компіляції, і ваш код через деякий час стає лускатим.
Ахмад Мусаві

@Stephen, будь ласка, поясніть мені, чому "В ідеалі асинхронний код ніколи не повинен мати справу з AggregateException, якщо він конкретно не хоче"
vcRobe

4
@vcRobe Тому що awaitзапобігає AggregateExceptionобгортці. AggregateExceptionбув розроблений для паралельного програмування, а не асинхронного програмування.
Стівен Клірі

2
> "Зачекайте правильно лише тоді, коли ви абсолютно впевнені, що завдання вже виконано." .... Тоді чому це називається Чекати?
Райан Ліч

4
@RyanTheLeach: Початкова мета Waitполягала в тому, щоб приєднатися до випадків паралелізму динамічної задачі Task . Використовувати його для очікування асинхронних Taskекземплярів небезпечно. Microsoft розглядала можливість введення нового типу "Обіцяння", але Taskнатомість вирішила використовувати існуючий ; компроміс повторного використання існуючого Taskтипу для асинхронних завдань полягає в тому, що у вас є кілька API, які просто не повинні використовуватися в асинхронному коді.
Стівен Клірі

12

Це має сенс, якщо timeoutTaskце продукт, про Task.Delayякий я вважаю, що це в книзі.

Task.WhenAnyповертається Task<Task>, де внутрішнє завдання є одним із тих, які ви передали як аргументи. Це можна переписати так:

Task<Task> anyTask = Task.WhenAny(downloadTask, timeoutTask);
await anyTask;
if (anyTask.Result == timeoutTask)  
  return null;  
return downloadTask.Result; 

В будь-якому випадку, тому що downloadTask це вже завершено, існує дуже незначна різниця між return await downloadTaskта return downloadTask.Result. Справа в тому, що останній викине AggregateExceptionбудь-який оригінальний виняток, на що в коментарях зазначив @KirillShlenskiy. Перший просто перекине оригінальний виняток.

У будь-якому випадку, де б ви не обробляли винятки, ви AggregateExceptionвсе одно повинні перевірити їхні внутрішні винятки, щоб дійти до причини помилки.

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