【问题标题】:How to get the inner exception from a cancelled task?如何从取消的任务中获取内部异常?
【发布时间】:2014-09-14 15:06:33
【问题描述】:

我有一个任务,如果发生异常,它会取消正在使用 sme 取消令牌的任务并引发异常:

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抛出的内部异常,怎么做?

【问题讨论】:

    标签: c# task-parallel-library


    【解决方案1】:

    如何使用 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。

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

    更新:您可以省略 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...
                        });
    

    【讨论】:

    • 返回冷任务违反了 TPL 准则。您应该使用 Task.Run 返回一个热门任务。
    • Yuval,你能按照我原来的语法发布代码吗?
    • 尤瓦尔:很高兴知道这一点。我更新了代码以使用热任务。 Anna R:你的意思是在没有 async/await 的情况下使用任务?
    • 是的,就像我发布的代码一样。是否可以像代码中那样从取消的任务中获取异常?
    • 我想我现在明白了。我相应地更新了代码。请问你为什么要这样使用它,而不是 async/await?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多