Использование задач с условными продолжениями - PullRequest
31 голосов
/ 11 апреля 2011

Я немного озадачен тем, как использовать Задачи с условными продолжениями.

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

void FunctionThrows() {throw new Exception("faulted");}

static void MyTest()
{

    var taskThrows = Task.Factory.StartNew(() => FunctionThrows());

    var onSuccess = taskThrows.ContinueWith(
                          prev => Console.WriteLine("success"), 
                          TaskContinuationOptions.OnlyOnRanToCompleted);

    var onError = taskThrows.ContinueWith(
                          prev => Console.WriteLine(prev.Exception),
                          TaskContinuationOptions.OnlyOnFaulted);

    //so far, so good



    //this throws because onSuccess was cancelled before it was started
    Task.WaitAll(onSuccess, onError);
}

Является ли это предпочтительным способом ветвления задачи / сбоя задачи?Кроме того, как я должен присоединиться ко всем этим задачам, предположим, что я создал длинную строку продолжений, каждое из которых имеет свою собственную обработку ошибок.из продолжений будет отменен из-за TaskContinuationOptions, и вызов Wait при отмене бросков задачи.Как мне присоединиться к ним, не получив исключение «Задача была отменена»?

Ответы [ 3 ]

11 голосов
/ 04 мая 2011

Я думаю, что ваша главная проблема в том, что вы говорите этим двум задачам: «Подождите», позвонив на

.
Task.WaitAll(onSuccess, onError);

Продолжения onSuccess и onError автоматически настраиваются для вас и будут выполнены после их предшествующей задачи завершено.

Если вы просто замените Task.WaitAll(...) на taskThrows.Start(); Я полагаю, вы получите желаемый результат.

Вот пример, который я собрал:

class Program
{
    static int DivideBy(int divisor) 
    { 
      Thread.Sleep(2000);
      return 10 / divisor; 
    }

    static void Main(string[] args)
    {
        const int value = 0;

        var exceptionTask = new Task<int>(() => DivideBy(value));

        exceptionTask.ContinueWith(result => Console.WriteLine("Faulted ..."), TaskContinuationOptions.OnlyOnFaulted | TaskContinuationOptions.AttachedToParent);
        exceptionTask.ContinueWith(result => Console.WriteLine("Success ..."), TaskContinuationOptions.OnlyOnRanToCompletion | TaskContinuationOptions.AttachedToParent);

        exceptionTask.Start();

        try
        {
            exceptionTask.Wait();
        }
        catch (AggregateException ex)
        {
            Console.WriteLine("Exception: {0}", ex.InnerException.Message);
        }

        Console.WriteLine("Press <Enter> to continue ...");
        Console.ReadLine();
    }
}
0 голосов
/ 31 октября 2012

Использование Task.WaitAny(onSuccess, onError);

0 голосов
/ 11 апреля 2011

Разве это не нормально?

Глядя на документацию MSDN, вы делаете это хорошо, и логика, которую вы реализуете, является разумной. Единственное, чего вам не хватает - это обернуть вызов WaitAll в обертку AggregateException следующим образом:

// Exceptions thrown by tasks will be propagated to the main thread
// while it waits for the tasks. The actual exceptions will be wrapped in AggregateException.
try
{
    // Wait for all the tasks to finish.
    Task.WaitAll(tasks);

    // We should never get to this point
    Console.WriteLine("WaitAll() has not thrown exceptions. THIS WAS NOT EXPECTED.");
}
catch (AggregateException e)
{
    Console.WriteLine("\nThe following exceptions have been thrown by WaitAll(): (THIS WAS EXPECTED)");
    for (int j = 0; j < e.InnerExceptions.Count; j++)
    {
        Console.WriteLine("\n-------------------------------------------------\n{0}", e.InnerExceptions[j].ToString());
    }
}

Вы можете прочитать больше здесь: http://msdn.microsoft.com/en-us/library/dd270695.aspx

По сути, перехват AggregatedException дает вам то же самое, что и завершение WaitAll. Это коллекция всех исключений, возвращаемых из ваших задач.

...