使用带有条件延续的任务

时间:2011-04-11 19:30:30

标签: c# .net-4.0 task-parallel-library

我对如何使用条件Continuations的任务感到困惑。

如果我有任务,然后我想继续处理成功和错误的任务,然后等待那些完成。

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);
}

这是执行任务成功/失败分支的首选方式吗?另外,我应该如何加入所有这些任务,假设我创建了一长串延续,每个都有自己的错误处理。

  //for example
  var task1 = Task.Factory.StartNew(() => ...)
  var task1Error = task1.ContinueWith(  //on faulted
  var task2  = task1.ContinueWith(      //on success
  var task2Error = task2.ContinueWith(  //on faulted
  var task3 = task2.ContinueWith(       //on success
  //etc

在这些问题上调用WaitAll总是会抛出,因为某些延续将因TaskContinuationOptions而被取消,并且在取消的任务上调用Wait会抛出。 如何在没有得到“任务被取消”例外的情况下加入这些例外“?

3 个答案:

答案 0 :(得分:10)

我认为你的主要问题是你通过拨打

告诉这两项任务“等待”
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();
    }
}

答案 1 :(得分:0)

这不正常吗?

查看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相同的功能。它是从您的任务返回的所有异常的集合。

答案 2 :(得分:0)

使用Task.WaitAny(onSuccess, onError);