Ответ 1
Я рекомендую использовать await
, а не ContinueWith
. В то время как на высоком уровне они очень похожи, у них также есть различное поведение по умолчанию.
Когда вы используете ContinueWith
, вы выбираете абстракцию более низкого уровня. В частности, вот некоторые "опасные точки", поэтому я не рекомендую использовать ContinueWith
, если метод действительно не прост (или ваше имя: Stephen Toub):
- Исключения из методов
async Task
помещаются в возвращаемую задачу; исключения, полученные из методов неasync
, распространяются напрямую. -
await
по умолчанию возобновит методasync
в том же "контексте". Этот "контекст"SynchronizationContext.Current
, если он не равенnull
, и в этом случае этоTaskScheduler.Current
. Это означает, что если вы вызываетеMyAsync
в потоке пользовательского интерфейса (или в контексте запроса ASP.NET), тогдаMyContinuation
также будет выполняться в потоке пользовательского интерфейса (или в том же контексте запроса ASP.NET). Я объясняю это более в своем блоге. - Вы всегда должны указывать планировщик для
ContinueWith
; в противном случае он подберетTaskScheduler.Current
, что может вызвать удивительное поведение. Я подробно описываю эту проблему в своем блоге. Это сообщение оStartNew
; ноContinueWith
имеет ту же самую "планировщик по умолчанию по умолчанию", описанную в этой статье. -
await
использует соответствующие флаги поведения и оптимизации, которые по умолчанию не установлены вContinueWith
. Например, он используетDenyChildAttach
(чтобы асинхронные задачи не были ошибочно использованы в качестве параллельных задач) иExecuteSynchronously
(оптимизация).
Короче говоря, единственной причиной использования ContinueWith
для асинхронных задач является сохранение чрезвычайно небольшого количества времени и памяти (из-за отсутствия служебных обязанностей конечного автомата async
), а взамен ваш код менее читабельный и поддерживаемый.
С очень простым примером вы можете уйти от него; но, как указал Джон Скит, как только у вас появятся циклы, код ContinueWith
просто взрывается по сложности.