簡體   English   中英

任務未處理的異常

[英]Task unhandled exceptions

我試圖了解在任務對象中拋出但從未處理過的異常發生了什么。

在MSDn上,據說:

如果您不等待傳播異常的任務或訪問其Exception屬性,則在對任務進行垃圾回收時,將根據.NET異常策略升級異常。

所以我不太明白這些異常會以何種方式影響程序流程。 我認為這些異常應該在垃圾收集后立即中斷執行。 但我無法設計這種行為。 在以下代碼段中,拋出的異常不會顯示。

 \\Do something ...
 Task.Run (()=> {throw new Exception("Exception in the task!");});
\\Do something else

請問,任何人都可以解釋如何處理未處理的任務異常,以及它們如何影響程序流程。

您正在描述.NET 4中的行為,但是您很難強制執行垃圾收集並實際觀察該行為。 以下引自斯蒂芬·圖布(Stephen Toub)關於這一主題的優秀文章,應該更清楚:

任務跟蹤是否“觀察到未處理的異常”。在此上下文中,“觀察”意味着代碼以某種方式與Task連接,以便至少知道異常。 這可能是在Task上調用Wait / WaitAll。 任務完成后,可以檢查Task的Exception屬性。 或者它可能正在使用Task的Result屬性。 如果一個任務看到它的異常已經以某種方式被觀察到,那么生活是美好的。 但是,如果刪除了對Task的所有引用(使Task可用於垃圾收集),並且如果尚未觀察到它的異常,則Task知道永遠不會觀察到它的異常。 在這種情況下,Task利用finalization,並使用輔助對象在終結器線程上傳播未處理的異常。 使用前面描述的行為,終結器線程上的該異常將被取消處理並調用默認的未處理異常邏輯,即記錄問題並使進程崩潰。

他還提出了兩個有用的擴展方法來處理“即發即棄”任務中的異常:一個忽略異常,另一個立即崩潰進程:

public static Task IgnoreExceptions(this Task task)
{
    task.ContinueWith(c => { var ignored = c.Exception; },
        TaskContinuationOptions.OnlyOnFaulted |
        TaskContinuationOptions.ExecuteSynchronously |
        TaskContinuationOptions.DetachedFromParent);
    return task;
}

public static Task FailFastOnException(this Task task)
{
    task.ContinueWith(c => Environment.FailFast(“Task faulted”, c.Exception),
        TaskContinuationOptions.OnlyOnFaulted |
        TaskContinuationOptions.ExecuteSynchronously |
        TaskContinuationOptions.DetachedFromParent);
    return task;
}

在.NET 4.5中 ,默認行為已更改。 再一次,引自另一個Stephen Toub關於這個主題的帖子 (感謝mike z在評論中引起我的注意):

為了使開發人員更容易編寫基於Tasks的異步代碼,.NET 4.5更改了未觀察到的異常的默認異常行為。 雖然未觀察到的異常仍會導致引發UnobservedTaskException事件(不會這樣做會發生重大變化),默認情況下進程不會崩潰。 相反,無論事件處理程序是否觀察到異常,異常將在引發事件后最終被吃掉。 但是,可以配置此行為。

請注意上面的代碼不太正確。 您必須返回指向task.ContinueWith的指針,而不是傳入的任務:

public static Task IgnoreExceptions(this Task task)
{
    var t = task.ContinueWith(c => { var ignored = c.Exception; },
        TaskContinuationOptions.OnlyOnFaulted |
        TaskContinuationOptions.ExecuteSynchronously);
    return t;
}

編輯:

這很有挑戰性,因為它取決於您如何將呼叫鏈接在一起。 例如,以下調用無法按預期方式工作:

public Task MyServiceCall()
{
  return Task.Run(() => DoSomething()).IgnoreExceptions();
}

此方法確實會拋出異常,因為接受的答案返回初始任務(而不是觀察異常的任務)。 這可能是與其他調用,如問題.Wait.WhenAll等有人可能會認為該任務將永遠不會丟,但它可以。

但是,我建議的更改將破壞以下內容:

public void SomeMethod()
{
  var myTask = new Task(() => ...);
  myTask.IgnoreExceptions().Start();
}

在內部,我們決定廢棄這種擴展方法,因為它太混亂了!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM