簡體   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