Правильный способ использования. ContinueWith для задач

Поэтому мне недавно сообщили, что я использую мой .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");
    });
}

Ответ 1

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

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

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

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

Ответ 2

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

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

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

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

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

Ответ 3

Кто вам сказал, что?

Цитата 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");
    });

Ответ 4

Из MSDN в Task.Continuewith

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

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

Ответ 5

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