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

本文关键字:条件 错误条件 结束 并行任务 链接 | 更新日期: 2023-09-27 18:18:22

我正在努力了解c#中并行任务的一些语法和结构,特别是链接多个任务和处理错误。

我希望创建的具体步骤顺序是:

  • 生成一个并行任务并立即返回UI。
  • 并行任务中:
    • Do 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的实例似乎没有异常,即使我手动从Process2()中抛出一个异常。查看MSDN关于处理异常的文章,它说要这样做:

try
{
    task.Wait();
}
catch (AggregateException ex)
{
    // Handle exceptions from a collection on ex
}

然而,我试过了,它似乎也没有例外。此外,通过在主线程中调用.Wait(),我否定并行性,只是阻塞吗?在我的测试中似乎是这样的。

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

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

请注意,如果您使用的是。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线程中启动这个,那么SuccessCondition和ErrorCondition也会在UI线程中发生。这里的一个功能区别是,您捕获的异常将不是AggregateException;它将是等待调用失败时抛出的实际异常。

您需要添加一个延续到所有的任务,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); 
在c# 5.0中,通过async方法,任务的错误处理变得更加容易。它允许你将代码转换成这样:
public static async Task Foo()
{
    try
    {
        await Task.Run(Process1())
        await Task.Run(Process2())
        await Task.Run(Process3())
        SuccessCondition();
    }
    catch (SomeExceptionType ex)
    {
        HandleException(ex);
    }
}

这个功能就像你想象的那样,根据你的要求,这是很棒的。