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

C# 如何从已取消的任务中获取内部异常?,c#,task-parallel-library,C#,Task Parallel Library,我有一个任务,如果出现异常,将取消使用sme取消令牌的任务并引发异常: var cancellationTokenSource = new CancellationTokenSource(); var task1 = Task.Factory.StartNew( () => { try {

我有一个任务,如果出现异常,将取消使用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抛出的内部异常,以及如何实现它?

使用异步/等待和常规异常处理如何?不过它需要.NET4.5。类似这样的情况(我不知道任务之间必须如何交互,因此您的代码可能最终看起来完全不同):

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

更新:更新了代码以使用任务创建任务。运行方法而不是使用冷任务

更新:您可以省略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返回一个热任务。改为运行
。Yuval,你能按照我原来的语法发布代码吗?Yuval:很高兴知道。我更新了代码以使用热任务。安娜R:你的意思是使用没有异步/等待的任务?是的,就像我发布的代码一样。有没有可能像代码中那样从已取消的任务中获取异常?我想我现在就得到了。我相应地更新了代码。请问您为什么要这样使用它,而不是async/await?
// 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...
                    });