如何更好地理解"异步 - 处理多个异常"一文中的代码/语句?

Gen*_*нин 3 c# exception-handling task-parallel-library async-await async-ctp

运行以下C#控制台应用程序

class Program
{  static void Main(string[] args)
   {  Tst();
      Console.ReadLine();
   }
   async static Task  Tst()
   {
       try
       {
           await Task.Factory.StartNew
             (() =>
                {
                   Task.Factory.StartNew
                       (() =>
                         { throw new NullReferenceException(); }
                         , TaskCreationOptions.AttachedToParent
                        );
               Task.Factory.StartNew
                       (  () =>
                               { throw new ArgumentException(); }
                               ,TaskCreationOptions.AttachedToParent
                       );
                }
             );
    }
    catch (AggregateException ex)
    {
        // this catch will never be target
        Console.WriteLine("** {0} **", ex.GetType().Name);

//******  Update1 - Start of Added code
        foreach (var exc in ex.Flatten().InnerExceptions)
        {
             Console.WriteLine(exc.GetType().Name);
        }
//******  Update1 - End of Added code
    }
    catch (Exception ex)
    {
       Console.WriteLine("## {0} ##", ex.GetType().Name);
    }
 }
Run Code Online (Sandbox Code Playgroud)

产生输出:

** AggregateException **
Run Code Online (Sandbox Code Playgroud)

虽然,上面的代码是从"异步 - 处理多个异常"博客文章中再现第一个片段 ,该文章讲述了它:

以下代码将捕获单个NullReferenceException或ArgumentException异常(将忽略AggregateException)

问题出在哪儿:

  1. 这篇文章错了吗?
    哪些代码/语句以及如何更改以便正确理解它?
  2. 我在复制文章的第一个代码片段时犯了错误?
  3. 这是由于.NET 4.0/VS2010异步CTP扩展中的一个错误,我正在使用?

Update1(响应svick的回答)

添加代码后

//******  Update1 - Start of Added code
        foreach (var exc in ex.Flatten().InnerExceptions)
        {
             Console.WriteLine(exc.GetType().Name);
        }
//******  Update1 - End of Added code
Run Code Online (Sandbox Code Playgroud)

产生的输出是:

** AggregateException **
NullReferenceException
Run Code Online (Sandbox Code Playgroud)

所以,同样评论马特史密斯:

AggregateException被抓,只包含抛出的异常之一(无论是NullReferenceException还是 ArgumentException取决于孩子的任务的执行顺序)

最有可能的是,这篇文章仍然是正确的,或者至少非常有用.我只需要了解如何更好地阅读/理解/使用它

Update2(响应svick的回答)

执行svick的代码:

internal class Program
{
  private static void Main(string[] args)
  {
    Tst();
    Console.ReadLine();
  }

  private static async Task Tst()
  {
    try
    {
      await TaskEx.WhenAll
        (
          Task.Factory.StartNew
            (() =>
               { throw new NullReferenceException(); }
            //, TaskCreationOptions.AttachedToParent
            ),
          Task.Factory.StartNew
            (() =>
               { throw new ArgumentException(); }
            //,TaskCreationOptions.AttachedToParent
            )

        );
    }
    catch (AggregateException ex)
    {
      // this catch will never be target
      Console.WriteLine("** {0} **", ex.GetType().Name);

      //******  Update1 - Start of Added code
      foreach (var exc in ex.Flatten().InnerExceptions)
      {
        Console.WriteLine("==="+exc.GetType().Name);
      }
      //******  Update1 - End of Added code
    }
    catch (Exception ex)
    {
      Console.WriteLine("## {0} ##", ex.GetType().Name);
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

生产:

## NullReferenceException ##
Run Code Online (Sandbox Code Playgroud)

输出.

为什么不AggregateException生产或捕获?

svi*_*ick 6

这篇文章错了.当您运行代码时,awaited Task包含一个如下所示的异常:

AggregateException
  AggregateException
    NullReferenceException
  AggregateException
    ArgumentException
Run Code Online (Sandbox Code Playgroud)

这里有什么await(或者更具体地说TaskAwaiter.GetResult())是它需要外部AggregateException并重新抛出它的第一个子异常.在这里,那是另一个AggregateException,所以这就是抛出的东西.

的代码,其中一个实施例Task具有多个异常和它们中的一个被直接重新抛出后await是使用Task.WhenAll()代替的AttachedToParent:

try
{
    await Task.WhenAll(
        Task.Factory.StartNew(() => { throw new NullReferenceException(); }),
        Task.Factory.StartNew(() => { throw new ArgumentException(); }));
}
catch (AggregateException ex)
{
    // this catch will never be target
    Console.WriteLine("** {0} **", ex.GetType().Name);
}
catch (Exception ex)
{
    Console.WriteLine("## {0} ##", ex.GetType().Name);
}
Run Code Online (Sandbox Code Playgroud)

  • +1,要突出显示:捕获的`AggregateException`,仅包含*抛出的异常**(NullReferenceException`或`ArgumentException`取决于子任务`执行的顺序) (2认同)
  • @Геннадий-Ванин您的更新与我的回答并不矛盾.就像我说的那样,`await`引发的异常不是外部的`AggregateException`,它是内部的第一个.因此,在捕获的异常上调用`Flatten()`只返回一个异常.但是如果将`Task`保存到变量中并调用`task.Exception.Flatten()`,那将返回两个异常. (2认同)