C# 任务异步模式和错误/异常/取消处理

C# 任务异步模式和错误/异常/取消处理,c#,exception-handling,task-parallel-library,C#,Exception Handling,Task Parallel Library,我经常在一些应用程序中使用顶级函数,比如 public Result5 ProcessAll() { var result1 = Process1(); var result2 = Process2(); var result3 = Process3(result1); var result4 = Process4(result1, result2); return Process5(result1, result2, result3, result4);

我经常在一些应用程序中使用顶级函数,比如

public Result5 ProcessAll() {
    var result1 = Process1();
    var result2 = Process2();
    var result3 = Process3(result1);
    var result4 = Process4(result1, result2);
    return Process5(result1, result2, result3, result4);
}
流程*功能的常见功能有:

  • IO绑定(数据库、文件系统、Web服务)
  • 可能会抛出刚刚在调用堆栈中传播的异常
  • 可能会为某些非异常错误返回错误,这些错误应停止处理并返回
顶级函数也在后台线程上运行,可以取消。 这意味着完整的实现看起来像

public Result5 ProcessAll(CancellationToken cancellationToken) {
    Result1 result1 = Process1();

    if (result1 == null)
        return null;
    cancellationToken.ThrowIfCancellationRequested();

    Result2 result2 = Process2();

    if (result2 == null)
        return null;
    cancellationToken.ThrowIfCancellationRequested();

    Result3 result3 = Process3(result1);

    if (result3 == null)
        return null;
    cancellationToken.ThrowIfCancellationRequested();

    Result4 result4 = Process4(result1, result2);

    if (result4 == null)
        return null;
    cancellationToken.ThrowIfCancellationRequested();

    return Process5(result1, result2, result3, result4);
}
现在让我们假设我需要通过尽可能多地并行运行来加快速度

还假设Process*函数实现任务异步模式,并使用IO完成端口或类似端口

我还没有找到适合这个的好模式。
如果我忽略错误/异常/取消,它会是这样的

public Result5 ProcessAll(CancellationToken cancellationToken) {
    Task<Result1> task1 = Process1Async();
    Task<Result2> task2 = Process2Async();

    Task<Result3> task3 = task1.ContinueWith(_ => Process3Async(task1.Result)).Unwrap();

    Task<Result4> task4 = Task.Factory.ContinueWhenAll(new[] { task1, task2 }, 
                                                       _ => Process4Async(task1.Result, task2.Result)).Unwrap();

    // This will trigger all exceptions captured
    Task.WaitAll(new[] { task1, task2, task3, task4 });

    return Process5(task1.Result, task2.Result, task3.Result, task4.Result);
}
public Result5 ProcessAll(CancellationToken CancellationToken){
任务task1=Process1Async();
任务task2=Process2Async();
Task task3=task1.ContinueWith(=>Process3Async(task1.Result)).Unwrap();
Task task4=Task.Factory.ContinueWhenAll(新[]{task1,task2},
_=>Process4Async(task1.Result,task2.Result)).Unwrap();
//这将触发捕获的所有异常
Task.WaitAll(新[]{task1,task2,task3,task4});
返回进程5(task1.Result、task2.Result、task3.Result、task4.Result);
}
(我知道这可以像运行task4 synchronous一样进行优化,WaitAll不是必需的,但我只是在这里展示一种模式)

如果我现在尝试处理错误和异常,它可能如下所示:

public Result ProcessAll(CancellationToken cancellationToken) {
    Task<Result1> task1 = Process1Async();
    Task<Result2> task2 = Process2Async();

    // Process 3 should not run if task1 or task2 failed or returned error
    Task<Result3> task3 = task1.ContinueWith(_ => {
         if (task1.IsFaulted || task1.Result == null)
             return null;
         if (task2.IsFaulted || (task2.IsCompleted && task2.Result == null)
             return null;
         return Process3Async(task1.Result);
    }).Unwrap();

    // Process4 should not start if any of Process1,Process2 or Process3 returned error or throw exception
    Task<Result4> task4 = Task.Factory.ContinueWhenAll(new[] { task1, task2 }, _ => {
                                                       if (task1.Faulted || task1.Result == null)
                                                           return null;
                                                       if (task2.Faulted || task2.Result == null)
                                                           return null;
                                                       if (task3.Faulted || (task3.IsCompleted && task3.Result == null))
                                                           return null;
                                                       return Process4Async(task1.Result, task2.Result)).Unwrap();

    Task.WaitAll(new[] { task1, task2, task3, task4 });
    if (task1.Result == null || 
        task2.Result == null || 
        task3.Result == null || 
        task4.Result == null)
        return null;
    return Process5(task1.Result, task2.Result, task3.Result, task4.Result);
}
public Result ProcessAll(CancellationToken CancellationToken){
任务task1=Process1Async();
任务task2=Process2Async();

//如果task1或task2失败或返回错误,则不应运行进程3 任务task3=task1.ContinueWith(=>{ if(task1.IsFaulted | | task1.Result==null) 返回null; if(task2.IsFaulted | | |)(task2.IsCompleted&&task2.Result==null) 返回null; 返回Process3Async(task1.Result); }).Unwrap(); //如果Process1、Process2或Process3中的任何一个返回错误或引发异常,则不应启动Process4 Task task4=Task.Factory.ContinueWhenAll(新[]{task1,task2},=>{ if(task1.Faulted | | task1.Result==null) 返回null; if(task2.Faulted | | task2.Result==null) 返回null; if(task3.Faulted | | |(task3.IsCompleted&&task3.Result==null)) 返回null; 返回Process4Async(task1.Result,task2.Result)).Unwrap(); Task.WaitAll(新[]{task1,task2,task3,task4}); 如果(task1.Result==null | | task2.Result==null | | task3.Result==null | | task4.Result==null) 返回null; 返回进程5(task1.Result、task2.Result、task3.Result、task4.Result); }
现在我需要填写取消支票:-)

我现在的问题是:
早期任务中的所有这些失败、错误和取消检查都很容易出错,而且扩展性不强。
我是否遗漏了一些重要的内容,并以错误的方式进行了操作?

我您只能在所有早期流程完成后启动流程3、4和5,但您与这些流程没有并行性。因此,您不需要为它们使用任务。您只需要为前两个流程使用任务,这会使问题更容易解决。如果您选择将它们作为任务启动这些任务将始终等待前一个任务,以这种方式删除任何并行性。

I您只能在所有早期进程都已完成时启动进程3、4和5,但您与它们没有并行性。因此,您不需要为它们使用任务。您只需要为前两个进程使用任务,这会导致问题更简单。如果您选择将它们作为任务启动,这些任务将始终等待前置任务,以这种方式消除任何并行性。

您是否尝试过这些链接?这些链接已经很好地覆盖了该字段,但不确定它是否完全适用于您所拥有的内容?并且-我认为您应该查看一下(如果尚未使用)。我假设您不能使用C#5,r是吗?@svick.Net 3.5和4版本。Wait简化了并行运行吗?我的理解是,Wait主要是异步工作(即在本例中实现流程*方法).你有没有举个例子?你有没有尝试过这些链接?这些链接已经很好地涵盖了这个领域,但不确定它是否完全适用于你所拥有的内容。我想你应该看看,如果你还没有。我假设你不能使用C#5,对吧?@svick.Net 3.5和4。Wait会简化并行运行吗?我的理解是,Wait I这主要是关于异步工作(即在本例中实现Process*方法)。您有示例吗?但这里不是这样。流程3不依赖于2,流程3和流程4可以同时运行。“如果task1或task2失败或返回错误,则流程3不应运行”。这意味着您只能在#1和#2都已完成的情况下才能判断是否要启动它。还是不?据我所知,如果进程2已完成并出现错误,则不应启动进程3,但如果进程2仍在运行,则可以启动它。至少问题中的最后一段代码是这样做的。示例代码很难等待ng代表#1,有一个错误,它应该等待#2,但没有-它有一个竞争条件。它正在检查#2的结果,而不等待结果可用。Svic