发生异常时如何打破任务链?

时间:2017-01-10 09:37:34

标签: c# .net multithreading task multitasking

我创造了作品链。他们要在我的额外线程内工作。为此,我使用Task。此外,如果发生任何异常,我想打破链的工作并将其抛入调用线程。但我发现我的链条没有被破坏,而act2 act3也已完成。

我该如何解决?

enter image description here

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

2 个答案:

答案 0 :(得分:2)

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

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

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

即使您使用 async / await 关键字,此方法仍然有效(但您可以摆脱 GetAwaiter 调用)

答案 1 :(得分:2)

代码试图以非常规方式使用任务,几乎就像它们是线程一样。它们不是 - 任务是一个将被安排在线程池线程上运行的作业,而不是线程本身。调用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;

您无需明确访问awaiter。对.Result的最终调用将返回整数结果,如果之前的任务之一出现故障,则抛出AggregateException