Рекомендуется ли использовать prevTask.Wait () с ContinueWith (из библиотеки задач)?

88

Поэтому мне недавно сказали, что то, как я использую .ContinueWith для задач, было неправильным способом их использования. Мне еще предстоит найти доказательства этого в Интернете, поэтому я спрошу вас, ребята, и посмотрю, какой будет ответ. Вот пример того, как я использую .ContinueWith:

public Task DoSomething()
{
    return Task.Factory.StartNew(() =>
    {
        Console.WriteLine("Step 1");
    })
    .ContinueWith((prevTask) =>
    {
        Console.WriteLine("Step 2");
    })
    .ContinueWith((prevTask) =>
    {
        Console.WriteLine("Step 3");
    });
}

Теперь я знаю, что это простой пример, и он будет работать очень быстро, но предположим, что каждая задача выполняет более длительную операцию. Итак, мне сказали, что в .ContinueWith вам нужно сказать prevTask.Wait (); в противном случае вы могли бы выполнить работу до завершения предыдущей задачи. Это вообще возможно? Я предполагал, что моя вторая и третья задачи будут выполняться только после завершения их предыдущей задачи.

Что мне сказали, как писать код:

public Task DoSomething()
{
    return Task.Factory.StartNew(() =>
    {
        Console.WriteLine("Step 1");
    })
    .ContinueWith((prevTask) =>
    {
        prevTask.Wait();
        Console.WriteLine("Step 2");
    })
    .ContinueWith((prevTask) =>
    {
        prevTask.Wait();
        Console.WriteLine("Step 3");
    });
}
Travyguy9
источник
2
Не используйте StartNew blog.stephencleary.com/2013/08/startnew-is-dangerous.html
Крис Марисич,

Ответы:

115

Эххх .... Я думаю, что в некоторых из текущих ответов чего-то не хватает: что происходит с исключениями?

Единственная причина, по которой вы вызываете Waitпродолжение, - это наблюдение за потенциальным исключением из антецедента в самом продолжении. То же самое произойдет, если вы получили доступ Resultв случае, Task<T>а также если вы вручную получили доступ к Exceptionсвойству. Честно говоря, я бы не стал звонить Waitили обращаться к вам, Resultпотому что в случае исключения вы заплатите цену за повторное повышение, что не является ненужными накладными расходами. Вместо этого вы можете просто проверить IsFaultedсобственность на антецеденте Task. В качестве альтернативы вы можете создавать разветвленные рабочие процессы, связывая в цепочку несколько одноуровневых продолжений, которые срабатывают только в случае успеха или неудачи с помощью TaskContinuationOptions.OnlyOnRanToCompletionи TaskContinuationOptions.OnlyOnFaulted.

Теперь нет необходимости наблюдать исключение предшествующего события в продолжении, но вы можете не захотеть, чтобы ваш рабочий процесс продвигался вперед, если, скажем, «Шаг 1» не удался. В этом случае: указание TaskContinuationOptions.NotOnFaultedна ваши ContinueWithвызовы предотвратит запуск логики продолжения.

Имейте в виду, что если ваши собственные продолжения не наблюдают за исключением, человек, ожидающий завершения этого рабочего процесса в целом, будет наблюдать за ним. Либо они находятся Waitв Taskвосходящем потоке, либо добавили собственное продолжение, чтобы знать, когда оно будет завершено. Если это последнее, то при их продолжении необходимо использовать вышеупомянутую логику наблюдения.

Дрю Марш
источник
2
Наконец-то кто-то дал правильный ответ. @ Travyguy9 Пожалуйста, прочтите этот ответ @DrewMarsh и узнайте больше оTaskContinuationOptions
Джаспере,
2
Отличный ответ, я искал: «Имейте в виду, что если ваши собственные продолжения не наблюдают за исключением, человек, который ожидает завершения этого рабочего процесса в целом, будет наблюдать за ним». Один вопрос: когда ваша задача не ожидает, кто будет официантом по умолчанию? (Не смог найти на это ответа)
Тибо Д.
20

Вы правильно его используете.

Создает продолжение, которое выполняется асинхронно после завершения целевой Задачи.

Источник: метод Task.ContinueWith (действие как MSDN)

Необходимость вызывать prevTask.Wait()при каждом Task.ContinueWithвызове кажется странным способом повторить ненужную логику - то есть делать что-то, чтобы быть «супер-уверенным», потому что вы на самом деле не понимаете, что делает определенный фрагмент кода. Как проверка на нуль, просто чтобы бросить туда, ArgumentNullExceptionгде он все равно был бы брошен.

Итак, нет, тот, кто сказал вам это, неправ и, вероятно, не понимает, почему Task.ContinueWithсуществует.

Андерс Арпи
источник
16

Кто тебе это сказал?

Цитата из MSDN :

Создает продолжение, которое выполняется асинхронно после завершения целевой Задачи.

Кроме того, какова была бы цель « Продолжить с», если бы он не ждал завершения предыдущей задачи?

Вы даже можете проверить это самостоятельно:

Task.Factory.StartNew(() =>
    {
        Console.WriteLine("Step 1");
        Thread.Sleep(2000);
    })
    .ContinueWith((prevTask) =>
    {
        Console.WriteLine("I waited step 1 to be completed!");
    })
    .ContinueWith((prevTask) =>
    {
        Console.WriteLine("Step 3");
    });
ken2k
источник
5

Из MSDN наTask.Continuewith

Возвращенная задача не будет запланирована для выполнения, пока текущая задача не будет завершена. Если критерии, указанные в параметре ContinuationOptions, не выполняются, задача продолжения будет отменена, а не запланирована.

Я думаю, что то, как вы ожидаете, что это будет работать в первом примере, является правильным.

mclark1129
источник
2

Вы также можете рассмотреть возможность использования Task.Run вместо Task.Factory.StartNew.

Сообщение в блоге Стивена Клири и сообщение Стивена Туба, на которое он ссылается, объясняют различия. В этом ответе также есть обсуждение .

Bizcad
источник
4
Проголосовали против, потому что это не касается фактического вопроса. Это добавляет некоторую ценность, но это должен быть комментарий.
Sinaesthetic
0

Получая доступ, Task.Resultвы фактически выполняете аналогичную логикуtask.wait

Самех
источник
Да. Мы можем избежать использования метода Wait (). Но это работает только с задачами результата, например Task <bool>
Александр Ульмаскулов
Проголосовали против, потому что это не касается фактического вопроса. Это добавляет некоторую ценность, но это должен быть комментарий.
Sinaesthetic