我捕获的AggregateException没有我期望的异常。

4
我正在使用任务并行库来设置一系列任务,如下所示,但我遇到了一个奇怪的异常处理体验,我不明白其中的原因。
我使用 Parallel.ForEach 并调用包含以下方法调用的操作。这个 Parallel.ForEach 被包裹在 try...catch(AggregateException) 中,当出现异常 - 就像在其中一个并行分支中发生的 SchemaValidation 异常一样 - 我会期望在 AggregateException 中看到它。
然而,我得到的是 '任务已取消' - TaskCanceledException。我的 SchemaValidationException 去哪了?
        private static void ProcessChunk(Task<ISelectedChunk> selectionTask, 
                                     IRepository repository, 
                                     IIdentifiedExtractChunk identifiedExtractChunk, 
                                     IBatchRunConfiguration batchRunConfiguration, 
                                     IBatchRun batchRun, 
                                     ILog log, 
                                     IAuthenticationCertificate authenticationCertificate, 
                                     IFileSystem fileSystem,
                                     long batchRunRid)
    {
        var transformationTask = selectionTask.ContinueWith(TransformationFunction.Transformation(identifiedExtractChunk, batchRunConfiguration, batchRun),
                                                            TaskContinuationOptions.NotOnFaulted);

        var schemaValidationTask = transformationTask.ContinueWith(SchemaValidationFunction.SchemaValidationTask(batchRunConfiguration),
                                                                   TaskContinuationOptions.NotOnFaulted);

        var compressTask = schemaValidationTask.ContinueWith(CompressFunction.CompressTask(identifiedExtractChunk),
                                                             TaskContinuationOptions.NotOnFaulted);

        var encryptTask = compressTask.ContinueWith(EncryptionFunction.EncryptTask(authenticationCertificate),
                                                    TaskContinuationOptions.NotOnFaulted);

        var fileGenerationTask = encryptTask.ContinueWith(FileGenerationFunction.FileGenerationTask(identifiedExtractChunk, batchRunConfiguration, fileSystem),
                                                          TaskContinuationOptions.NotOnFaulted);
        // Take the time before we start the processing
        DateTime startBatchItemProcessing = DateTime.Now;

        // Start with the Selection Task
        selectionTask.Start();

        // And wait on the last task in the chain
        fileGenerationTask.Wait();

        // Take the time at the end of the processing
        DateTime endBatchItemProcessing = DateTime.Now;

        // Record all the relevant information and add it to the collection 
        IBatchChunkProcessed batchChunkProcessed = GetBatchItemProcessed(identifiedExtractChunk, batchRunRid, fileGenerationTask.Result, transformationTask.Result.Item2, startBatchItemProcessing, endBatchItemProcessing);
        BatchItemsProcessed.Add(batchChunkProcessed);

1
你为什么要在这里使用“Task”?为什么不按顺序依次执行所有方法呢? - svick
1
代码似乎与问题无关。发布捕获和处理异常的代码。 - Hans Passant
@svick - 传入的选择任务已经并行化。此外,这些是离散的构建块任务,可以在其他情境中轻松地重复使用,并可能成为根任务。 - Ciaran
@HansPassant - 问题不在于异常处理代码 - 无论我把它放在哪里,都会遇到同样的问题。 - Ciaran
1个回答

6

让我们简化一下你的代码:

var t1 = Task.Factory.StartNew(a1);
var t2 = t1.ContinueWith(a2, TaskContinuationOptions.NotOnFaulted);
var t3 = t2.ContinueWith(a3, TaskContinuationOptions.NotOnFaulted);

t3.Wait();

现在假设 a1 抛出一个异常。发生的情况是 t1 变为故障(t1.Status == TaskStatus.Faulted)。因此,t2 无法运行(因为是 NotOnFaulted),所以它将被取消。但这不是您预期的: t2 不会失败,而是会被取消(t2.Status == TaskStatus.Canceled)。但这意味着 t3 可以正常运行,如果它没有抛出异常,则 t3.Wait() 不会引发任何异常。
如何解决这个问题?首先,您可能不应该使用 TaskContinuationOptions.NotOnFaulted,而应该使用 TaskContinuationOptions.OnlyOnRanToCompletion。但这并不能解决“消失”的异常问题。要解决这个问题,我看到有两种可能性:
  1. 在每个延续开始时调用 Wait(),并且不使用任何 TaskContinuationOptions。这意味着您可能会得到一些包装在 AggregateException 中的异常,它本身被包装在另一个 AggregateException 中,依此类推。要解决这个问题,您可以使用 Flatten()Handle()

  2. 等待所有任务,使用 Task.WaitAll()WaitAll() 将抛出一个 AggregateException,其中包含原始异常以及因第一个异常而被取消的每个任务的 TaskCanceledException


我选择了Task.WaitAll选项 - 看起来最干净。现在我得到了我想要的异常以及TaskCanceledExceptions。非常感谢。 - Ciaran

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接