В чем разница, если таковая имеется, между Task.Delay.ContinueWith и async, ожидающими с Task.Delay? - PullRequest
0 голосов
/ 22 мая 2018

Это пример linqpad для двух способов асинхронного выполнения метода после небольшой задержки.Оба примера, похоже, делают одно и то же.Обычно я реализую первую версию (используя Task.Delay.ContinueWith), но я видел и вторую используемую реализацию (async await).Есть ли какая-либо разница между этими двумя реализациями?Рабочий пример Linqpad для этого сценария:

void Main()
{
   // Using Task.Delay.ContinueWith...
   Task.Delay(1000).ContinueWith(t => DoSomething());       

   // ... vs async await. Note that I'm not awaiting the task here
   DoSomethingAsync();
}

public void DoSomething()
{
    "Doing Something...".Dump();
}

public async Task DoSomethingAsync()
{
    await Task.Delay(1000);

    "Doing Something...".Dump();
}

После прочтения этого поста в блоге https://blogs.msdn.microsoft.com/pfxteam/2012/03/24/should-i-expose-asynchronous-wrappers-for-synchronous-methods/ я предположил, что первая реализация была «правильной», потому что DoSomethingAsync () действительно только выгружаетметод в пуле потоков и в сообщении в блоге говорится:

"Асинхронные методы не должны предоставляться исключительно с целью разгрузки: такие преимущества могут быть легко достигнуты потребителем синхронных методов с использованием функций, специально предназначенных для работы с синхроннымиметоды асинхронно, например, Task.Run. "

Однако этот ответ на StackOverflow предлагает второе решение:

Задержка, а затем выполнение Задачи

Есть ликакая-то реальная разница между двумя реализациями?Если реализация 'async await' также допустима (или даже более корректна), что следует сделать с возвращенной задачей?Я действительно не хочу ждать этого, это операция запуска и забывания, но я также хочу обрабатывать любые исключения, которые могут быть сгенерированы.

В первой реализации, которую я знаю, я мог обработать исключение, используя ContinueWith, OnlyOnFaults.

Ответы [ 2 ]

0 голосов
/ 22 мая 2018

Они похожи, но не совсем одинаковы.Например, в прецессии SynchronizationContext.Current продолжение метода async будет запланировано на этот контекст синхронизации, но ContinueWith не будет и будет выполняться в потоке пула потоков.Хотя, используя другую перегрузку ContinueWith, вы можете заставить ее делать то же самое:

.ContinueWith(t => DoSomething(), TaskScheduler.FromCurrentSynchronizationContext());

И вы можете запретить планирование контекста синхронизации в асинхронной версии с await yourTask.ConfigureAwait(false).

Тогда исключениеобработка отличается.В версии async исключение будет выдано напрямую, а если было несколько исключений (например, из await Task.WhenAll) - будет выдано только первое, а остальные будут проглочены.Трудно пропустить это исключение.

В версии ContinueWith выброшенное исключение представлено t.Exception, и это всегда AggregateException, поэтому вам нужно развернуть его.С другой стороны - все исключения есть (если их несколько) и ни одно не проглочено.Однако довольно легко забыть обработать это исключение.Например, в коде в вашем вопросе - вы не обрабатываете исключение в ContinueWith, поэтому продолжение DoSomething() будет выполнено в любом случае, независимо от того, было это исключение или нет.В версии async продолжение не выполняется в случае исключения.

Обе реализации являются «действительными».Вы не должны забывать обрабатывать исключения в обоих случаях.С ContinueWith - либо всегда проверяйте t.Exception, либо запланируйте отдельное продолжение с OnlyOnFaulted (и проверяйте только там).В случае версии async - завернуть корпус в блок try-catch.По характеру возникновения пожара и забывания - вы не можете обрабатывать исключения на месте вызова, но вам не следует полностью отказываться от них.

В вашем конкретном случае с выполнением метода после небольшой задержки я бы сказал, что это просто вопроспредпочтения (за исключением различий в захвате контекста синхронизации).Я лично предпочитаю ContinueWith, это выражает намерение более ясно, и вам не нужен отдельный метод с неясной семантикой.

0 голосов
/ 22 мая 2018

ContinueWith () позволяет связывать задачи в стиле «забей и забудь».В противном случае вам придется дождаться завершения задачи и затем выполнить следующую ожидаемую операцию (а «await» означает, что вам также нужно изменить сигнатуру вызывающего метода с помощью «async», что может быть не очень подходящим, скажем, в обработчиках событий)).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...