繁体   English   中英

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

[英]Why is only one from many exceptions from child tasks always propagated?

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

对我之前的问题“如何更好地理解“异步-处理多个异常”一文中的代码/语句”做了些修改?” 运行2个分离的内部嵌套附加(相关)子对象的C#控制台应用程序代码(更新:抱歉,开始一个问题,但由另一个问题结束!)任务:

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

似乎总是传播/捕获子任务之一的异常,并且只有一个例外。

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

在这种情况下,是否有可能同时捕获全部异常或全部异常?

您不应将父/子任务与async混合使用。 它们并非旨在一起使用。

svick已经回答了这个问题,作为他对其他问题的(正确)回答的一部分。 这是您如何想到的:

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

您可以通过保存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操作符仅传播其中之一,因此这就是为什么您仅捕获其中一个的原因。

从我可以推断出发生这种情况的原因是,等待信号指示该任务等待任务完成。 当引发异常时,任务完成(因为异常使它崩溃),并且该异常向外传播到将捕获该异常的异步函数。 这意味着您将始终在此设置中遇到一个异常。

要始终捕获两者,请删除await,而改用Task.Factory.StartNew(..)。Wait(); Wait函数将保留所有子进程的计数,直到所有子进程完成后才返回。 由于抛出了多个异常(每个孩子一个),因此将它们捆绑在一个新的AggregateException中,稍后将其捕获并将其子对象展平并打印内部异常。 这应该给您输出:

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM