如何从取消的任务中获取内部异常?

时间:2014-09-14 15:06:34

标签: c# task-parallel-library

我有一项任务,如果发生异常,则取消使用sme cancelation令牌的任务并抛出异常:

var cancellationTokenSource = new CancellationTokenSource();

var task1 = Task.Factory.StartNew(
                () =>
                    {
                        try
                        {
                            // Do work 
                        }
                        catch (Exception exception)
                        {                              
                            cancellationTokenSource.Cancel();

                            // rethrow the error
                            throw;
                        }     
                    },
                cancellationTokenSource.Token,
                TaskCreationOptions.None,
                taskScheduler); 

我有另一个task3(返回任务),它是task1和task2的延续,并使用相同的取消令牌:

return task3 =
                Task.Factory.ContinueWhenAll(
                    new[] { task1, task2 },
                    tasks =>
                        {                           
                           // Do work
                        },
                cancellationTokenSource.Token,
                TaskContinuationOptions.None,
                this.taskScheduler).Unwrap();

取消task1后,所有具有相同取消令牌的任务也会被取消。 我需要得到task1抛出的内部异常,它是如何完成的?

1 个答案:

答案 0 :(得分:2)

如何使用async / await和常规异常处理?但它需要.NET 4.5。这样的事情(我不知道任务必须如何相互影响,所以你的代码可能看起来完全不同):

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApplication1
{

    class Program
    {
        static void Main(string[] args)
        {
            new MyClass().RunAsyncMethods();
            Console.ReadLine();

        }

        public class MyClass
        {
            public async void RunAsyncMethods()
            {
                try
                {
                    var cancellationTokenSource = new CancellationTokenSource();

                    var task1 = RunFirstTaskAsync(cancellationTokenSource);
                    var task2 = RunSecondTaskAsync(cancellationTokenSource);

                    await Task.WhenAll(task1, task2);
                    await RunThirdTaskAsync(cancellationTokenSource);
                    Console.WriteLine("Done");
                }
                catch (Exception exc)
                {
                    Console.WriteLine(exc.Message);
                }
            }

            public Task RunFirstTaskAsync(CancellationTokenSource cancelSource)
            {
                return Task.Run(() =>
                {
                    try
                    {
                        Console.WriteLine("First Task is Running");
                        throw new Exception("Error happened in first task");
                    }
                    catch (Exception exception)
                    {
                        cancelSource.Cancel();

                        throw;
                    }
                },
                    cancelSource.Token);
            }

            public Task RunSecondTaskAsync(CancellationTokenSource cancelSource)
            {
                return Task.Run(
                    () =>
                    {
                        Console.WriteLine("Second Task is Running");
                    },
                    cancelSource.Token);
            }

            public Task RunThirdTaskAsync(CancellationTokenSource cancelSource)
            {
                return Task.Run(
                    () =>
                    {
                        Console.WriteLine("Third Task is Running");
                    },
                    cancelSource.Token);
            }
        }
    }
}

更新:我更新了代码以使用WhenAll。

UPDATE:更新了使用Task.Run方法创建任务的代码,而不是使用冷任务。

UPDATE:你可以省略async / await(尽管我认为这样更好)并且做一些老派的TPL错误处理。只是不对task3(返回任务)使用取消令牌,只针对它正在等待的任务。当它们完成时(通常,或通过异常/取消),你可以像这样处理task3中的异常:

// don't use the cancellation token for the third task as you used for the previous ones
var task3 = Task.Factory.ContinueWhenAll(
                    new[] { task1, task2 },
                    tasks =>
                    {
                        if (tasks[0].Exception != null)
                        {
                            tasks[0].Exception.Handle(exc =>
                            {
                                Console.WriteLine("First task failed :(");
                                return false; // signal that exception was handled, so it won't propagate
                            });
                            // add additional code here, or inside the Handle method above
                        }

                        if (tasks[1].Exception != null)
                        {
                            tasks[1].Exception.Handle(exc =>
                            {
                                Console.WriteLine("Second task failed :(");
                                return false; // signal that exception was handled, so it won't propagate
                            });
                            // add additional code here, or inside the Handle method above
                        }

                        // do the same for the rest of the tasks or iterate throught them with a foreach loop...
                    });