c# - 为什么子任务的众多异常中只传播其中之一?

标签 c# logging task-parallel-library async-await visual-studio-debugging

我正在努力更好地掌握 TPL 中异常和错误处理的基本原理(并且在 .NET 4.5 异步/等待任务中运气更好)

对我之前的问题略有修改"How to better understand the code/statements from "Async - Handling multiple Exceptions" article?" C# 控制台应用程序代码运行 2 个分离的内部嵌套 附加(依赖)子项(更新:抱歉,开始了一个问题,但以另一个问题结束!)任务:

class Program
{  
   static void Main(string[] args)
   {  Tst();
      Console.ReadLine();
   }
   async static Task  Tst()
   {
       try
       {
           await Task.Factory.StartNew
             (() =>
                {
                   Task.Factory.StartNew
                       (   () => { 
                                    Console.WriteLine("From 1st child");
                                    throw new NullReferenceException(); 
                                  }
                            , TaskCreationOptions.AttachedToParent
                        );
               Task.Factory.StartNew
                       (  () =>
                               { 
                                   Console.WriteLine("From 2nd child");
                                   throw new ArgumentException(); 
                               }
      ,TaskCreationOptions.AttachedToParent
                       );
                }
             );
    }
    catch (AggregateException ex)
    {
        Console.WriteLine("** {0} **", ex.GetType().Name);
        foreach (var exc in ex.Flatten().InnerExceptions)
        {
             Console.WriteLine(exc.GetType().Name);
        }
    }
    catch (Exception ex)
    {
       Console.WriteLine("## {0} ##", ex.GetType().Name);
    }
 } 

产生在以下之间交替(不确定地)的输出:

From 1st child
From 2nd child
** AggregateException **
ArgumentException

From 1t child
From 2nd child
** AggregateException **
NullReferenceException

似乎总是传播/捕获子任务之一中的一个且仅有一个异常。

为什么只传播/捕获一个异常?
如果总是捕获子任务中的任何异常或所有异常,我会更好地理解

在这种情况下,是否有可能同时捕获两个异常或都不捕获异常?

最佳答案

您不应将父/子任务与异步混合使用。它们并不是设计来一起使用的。

svick 已经在 his (correct) answer to your other question 中回答了这个问题。您可以这样想:

  • 每个内部 StartNew 都会获取一个异常,该异常被包装到 AggregateException 中并放置在返回的 Task 上。
  • 外部 StartNew 从其子任务中获取两个 AggregateException,并将其包装到其返回的 Task< 上的另一个 AggregateException
  • 当您等待一个任务时,会引发第一个内部异常。其他任何内容都将被忽略。

您可以通过保存 Task 并在 await 引发异常后检查它们来观察此行为:

async static Task Test()
{
    Task containingTask, nullRefTask, argTask;
    try
    {
        containingTask = Task.Factory.StartNew(() =>
        {
            nullRefTask = Task.Factory.StartNew(() =>
            {
                throw new NullReferenceException();
            }, TaskCreationOptions.AttachedToParent);
            argTask = Task.Factory.StartNew(() =>
            {
                throw new ArgumentException();
            }, TaskCreationOptions.AttachedToParent);
        });
        await containingTask;
    }
    catch (AggregateException ex)
    {
        Console.WriteLine("** {0} **", ex.GetType().Name);
    }
}

如果在 WriteLine 上放置断点,您可以看到两个子任务的异常都被放置在父任务上。 await 运算符仅传播其中之一,因此您只能捕获其中一个。

关于c# - 为什么子任务的众多异常中只传播其中之一?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16580125/

相关文章:

logging - VB6-如何在启动应用程序时在VB6中创建日志文件

c# - 何时使用 BlockingCollection 以及何时使用 ConcurrentBag 而不是 List<T>?

c# - 没有 SQL Server 的 Visual Studio 2012 安装

c# - 使用 Web 请求为函数编写单元测试

logging - 在hybris中将log4j替换为slf4j

c# - ConcurrentQueue<T>.TryPeek() 的用例是什么?

c# - 如何在 TransformBlock 之后写入对象?

c# - asp.net mvc本地化

c# - 如何在将加载到 Redis 的 LUA 脚本上重用代码?

logging - 在 log4net 中,如何使用相同的附加程序,只是文件名不同?