为什么只有来自子任务的许多异常中的一个总是传播?

Gen*_*нин 2 c# logging task-parallel-library visual-studio-debugging async-await

我正在努力更好地掌握TPL中异常和错误处理的基本原理(并且在.NET 4.5 async/await tasks中有更多运气)

稍微修改了我之前的问题"如何更好地理解来自"异步 - 处理多个异常"的文章中的代码/语句?" 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);
    }
 } 
Run Code Online (Sandbox Code Playgroud)

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

From 1st child
From 2nd child
** AggregateException **
ArgumentException
Run Code Online (Sandbox Code Playgroud)

From 1t child
From 2nd child
** AggregateException **
NullReferenceException
Run Code Online (Sandbox Code Playgroud)

似乎总是只有一个例外,其中一个子任务总是被传播/捕获.

为什么只传播/捕获一个异常?
如果没有或者更确切地说所有来自子任务的异常都被捕获,我会更好地理解

在这种情况下,是否有可能发现两个或没有例外?

Ste*_*ary 6

你不应该混合使用父/子任务async.他们不是为了团结而设计的.

svick已经回答了这个问题,作为他(正确)回答你的另一个问题的一部分.以下是您如何看待它:

  • 每个内部StartNew都有一个例外,它被包装成一个AggregateException并放在返回的上面Task.
  • 外部从其子任务中StartNew获取两个AggregateExceptions,它AggregateException在返回时包装到另一个中Task.
  • 当你awaita时Task,会引发第一个内部异常.其他任何人都被忽略了

您可以通过保存Tasks并在引发异常后检查它们来观察此行为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);
    }
}
Run Code Online (Sandbox Code Playgroud)

如果打开断点WriteLine,则可以看到两个子任务的异常都放在父任务上.该await运营商唯一的传播其中的一个,所以这就是为什么你只抓到一个.