将并行任务链接到结束条件或错误条件

时间:2013-06-14 18:20:33

标签: c# exception-handling task-parallel-library

我正试图围绕C#中的并行任务的一些语法和结构,特别是围绕chaining multiple taskshandling errors

我想要创建的具体步骤顺序是:

  • 生成并行任务并立即返回UI。
  • 在并行任务中:
    • Process1()
    • 如果Process1()完成且没有错误,请执行Process2()
    • 如果Process2()完成且没有错误,请执行Process3()
    • 如果所有任务都完成且没有错误,请执行SuccessCondition()
    • 如果任何任务导致错误,请执行ErrorCondition()

我的理解是,我将创建一个Task并调用ContinueWith()来链接更多任务,传入TaskContinuationOptions标志以确定何时执行该延续。此外,成功/错误条件将贯穿所有延续到最后。所以我现在正在尝试这个:

var task = new Task(() => Process1());
var task2 = task.ContinueWith(t => Process2(), TaskContinuationOptions.OnlyOnRanToCompletion);
var task3 = task2.ContinueWith(t => Process3(), TaskContinuationOptions.OnlyOnRanToCompletion);
var task4 = task3.ContinueWith(t => SuccessCondition(t), TaskContinuationOptions.OnlyOnRanToCompletion);
var task5 = task4.ContinueWith(t => ErrorCondition(t), TaskContinuationOptions.NotOnRanToCompletion);
task.Start();

它似乎表现得像预期的那样,除了在ErrorCondition()内,t的实例似乎没有例外,即使我手动从内部抛出一个,例如{{1} }}。查看用于处理异常的MSDN文章,它说:

Process2()

然而,我试过了,似乎也没有例外。另外,通过在主线程中调用try { task.Wait(); } catch (AggregateException ex) { // Handle exceptions from a collection on ex } ,我是否可以否定并行性并阻止?在我的测试中看起来就像这样。

所以我想我的问题是......链接相关任务和处理整体成功/错误情况的正确方法是什么?或者,如何正确捕获从这些任务中抛出的异常,同时仍然立即返回到UI?

2 个答案:

答案 0 :(得分:3)

您需要为所有任务1-4添加一个延续,并使用错误处理案例允许任何一个错误调用该函数。

为方便起见,您可以创建一个方法,将相同的延续添加到任务集合中。这是一个(根据需要随意添加其他重载的ContinueWith):

public static IEnumerable<Task> ContinueWith(this IEnumerable<Task> tasks
    , Action<Task> continuation, TaskContinuationOptions options)
{
    return tasks.Select(task => task.ContinueWith(continuation, options))
        .ToList();//important for this ToList to be here; 
    //we want the continuations to be added now, not when the result is iterated
}

这允许你写:

var errorTasks = new[]{task, task2, task3, task4}
    .ContinueWith(ErrorCondition, TaskContinuationOptions.NotOnRanToCompletion); 

使用async方法在C#5.0中使任务的错误处理变得更加容易。它允许您将代码转换为:

public static async Task Foo()
{
    try
    {
        await Task.Run(Process1())
        await Task.Run(Process2())
        await Task.Run(Process3())
        SuccessCondition();
    }
    catch (SomeExceptionType ex)
    {
        HandleException(ex);
    }
}

这就像您认为的那样,按照您的要求运行,这很棒。

答案 1 :(得分:3)

请注意,如果您使用的是.NET 4.5并且可以使用async / await,那么您可以更干净地执行此操作:

    public async Task DoProcess()
    {
        try
        {
            await Task.Run(Process1);
            await Task.Run(Process2);
            await Task.Run(Process3);
            SuccessCondition();
        }
        catch (Exception ex)
        {
            ErrorCondition(ex);
        }
    }

如果从UI线程启动它,则还会在UI线程上发生SuccessCondition和ErrorCondition。这里的一个功能区别是你捕获的异常不会是AggregateException;它将是在等待的呼叫失败期间抛出的实际异常。