发生异常时如何打断Task链?

How to break the chain of Task when an exception occurs?

我创建作品链。他们将在我的附加线程中工作。为此,我使用 Task。另外,如果发生任何异常,我想中断链的工作并将其抛出到调用线程中。但是我看到我的链条没有断, act2act3 也完成了。

我该如何解决?

using System;
using System.Threading.Tasks;

namespace Bushman.Sandbox.Threads {
    class Program {
        static void Main(string[] args) {
            Console.Title = "Custom thread";

            try {
                // First work
                Action act1 = () => {
                    for (int i = 0; i < 5; i++) {

                        // I throw the exeption here
                        if (i == 3) throw new Exception("Oops!!!");

                        Console.WriteLine("Do first work");
                    }
                };

                // Second work
                Action act2 = () => {
                    for (int i = 0; i < 5; i++)
                        Console.WriteLine("  Do second work");
                };

                // Third work
                Func<int> act3 = () => {
                    for (int i = 0; i < 5; i++)
                        Console.WriteLine("    Do third work");
                    return 12345;
                };

                Task task = new Task(act1);

                // Build the chain of the works
                var awaiter = task.ContinueWith(_ => act2(),
                    TaskContinuationOptions.ExecuteSynchronously)
                    .ContinueWith(_ => act3(),
                    TaskContinuationOptions.ExecuteSynchronously)
                    .GetAwaiter();

                Console.WriteLine("Work started...");

                // launch the chain
                task.Start();

                // Here I get some result
                int result = awaiter.GetResult(); // 12345

                if (task.IsCanceled || task.IsFaulted) {
                    throw task.Exception.InnerException;
                }

                Console.WriteLine("The result: {0}",
                    result.ToString());
            }
            catch (Exception ex) {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine(ex.Message);
                Console.ResetColor();
            }

            Console.WriteLine("Press any key for exit...");
            Console.ReadKey();
        }
    }
}

您必须使用 NotOnFaulted 任务继续选项。

由于 TaskContinuationOptions 用 Flags 属性修饰,您可以将 NotFaulted 与其他选项结合使用。

 var awaiter = task.ContinueWith(_ => act2(),
                TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.NotOnFaulted)
                .ContinueWith(_ => act3(),
                TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.NotOnFaulted)
                .GetAwaiter();

即使你使用了async/await关键字,这个方法仍然有效(但是你去掉了GetAwaiter调用)

代码试图以一种非常规的方式使用任务,就好像它们是线程一样。它们不是 - 任务是一项将被安排到 运行 线程池线程上的作业,而不是线程本身。调用 Task.Start 不会执行任何操作,它将 安排 其委托给线程上的 运行。这就是为什么永远不会使用构造函数创建任务的原因。

开始和协调任务的最简单方法是使用Task.Run和async/await,例如:

public static async Task<int> MyMethodAsync()
{
    try
    {
        await Task.Run(()=>act1());
        await Task.Run(()=>act2());
        var result=await Task.Run(()=>act3());
        return result;
    }
    catch (Exception exc)
    {
           //Do something
    }
}

您不能在控制台应用程序的 Main 函数上使用 async/await,因此您必须按以下方式调用该方法:

var result=MyMethodAsync().Result;

对任务调用 .Wait().Result 会重新抛出其中引发的任何异常。

没有 async/await,你需要使用ContinueWith并实际检查上一个任务的结果。如果你只是想停止处理,你可以通过 TaskContinuationOptions.NotOnFaulted :

var result = Task.Run(()=>act1())
                 .ContinueWith( t1=>act2(),TaskContinuationOptions.NotOnFaulted)
                 .ContinueWith( t2=>act3(),TaskContinuationOptions.NotOnFaulted)
                 .Result;

您不需要获得对等待者的显式访问权限。对 .Result 的最终调用将 return 整数结果或抛出 AggregateException 如果之前的任务之一出错