Может ли кто-нибудь объяснить, являются ли await
и ContinueWith
синонимами или нет, в следующем примере. Я пытаюсь использовать TPL впервые, читал всю документацию, но не понимаю разницы.
Жду :
String webText = await getWebPage(uri);
await parseData(webText);
ContinueWith :
Task<String> webText = new Task<String>(() => getWebPage(uri));
Task continue = webText.ContinueWith((task) => parseData(task.Result));
webText.Start();
continue.Wait();
Что предпочтительнее в определенных ситуациях?
c#
task-parallel-library
task
async-await
Харрисон
источник
источник
Wait
вызов во втором примере , то два сниппет будут ( в основном) эквивалентны.getWebPage
метод нельзя использовать в обоих кодах. В первом коде он имеетTask<string>
тип возврата, а во втором -string
тип возврата. так что в основном ваш код не компилируется. - если быть точным.Ответы:
Во втором коде вы синхронно ждете завершения продолжения. В первой версии метод вернется к вызывающему, как только он встретит первое
await
выражение, которое еще не завершено.Они очень похожи в том, что оба планируют продолжение, но как только поток управления становится даже немного сложным, это
await
приводит к гораздо более простому коду. Кроме того, как отметил Серви в комментариях, ожидание задачи будет «разворачивать» совокупные исключения, что обычно приводит к более простой обработке ошибок. Также использованиеawait
будет неявно запланировать продолжение в вызывающем контексте (если вы не используетеConfigureAwait
). Нет ничего, что нельзя было бы сделать «вручную», но с этим сделать это намного прощеawait
.Я предлагаю вам попробовать реализовать чуть большую последовательность операций с обоими
await
иTask.ContinueWith
- это может быть настоящим откровением.источник
await
болееContinueWith
.parseData
выполняется.Вот последовательность фрагментов кода, которые я недавно использовал, чтобы проиллюстрировать разницу и различные проблемы с использованием асинхронного решения.
Предположим, у вас есть обработчик событий в вашем приложении на основе графического интерфейса пользователя, который требует много времени, и поэтому вы хотите сделать его асинхронным. Вот синхронная логика, с которой вы начинаете:
LoadNextItem возвращает задачу, которая в конечном итоге даст результат, который вы хотите проверить. Если текущий результат - это тот, который вы ищете, вы обновляете значение некоторого счетчика в пользовательском интерфейсе и возвращаетесь из метода. В противном случае вы продолжите обработку дополнительных элементов из LoadNextItem.
Первая идея для асинхронной версии: просто используйте продолжения! И давайте пока проигнорируем часть зацикливания. Я имею в виду, что могло пойти не так?
Отлично, теперь у нас есть метод, который не блокирует! Вместо этого происходит сбой. Любые обновления элементов управления пользовательского интерфейса должны происходить в потоке пользовательского интерфейса, поэтому вам нужно будет это учитывать. К счастью, есть возможность указать, как следует планировать продолжения, и есть вариант по умолчанию для этого:
Отлично, теперь у нас есть метод, который не дает сбоев! Вместо этого он тихо терпит неудачу. Продолжения сами по себе являются отдельными задачами, их статус не привязан к статусу предшествующей задачи. Таким образом, даже если LoadNextItem выдает ошибку, вызывающий увидит только успешно завершенную задачу. Хорошо, тогда просто передайте исключение, если оно есть:
Отлично, теперь это действительно работает. Для одного предмета. Теперь, как насчет этого цикла. Оказывается, решение, эквивалентное логике исходной синхронной версии, будет выглядеть примерно так:
Или вместо всего вышеперечисленного вы можете использовать async, чтобы сделать то же самое:
Теперь это намного лучше, не так ли?
источник