1. 程式人生 > 其它 >TaskScheduler.UnobservedTaskException 與 TaskCompletionSource 中的異常處理

TaskScheduler.UnobservedTaskException 與 TaskCompletionSource 中的異常處理

背景問題

監控發現 TaskScheduler.UnobservedTaskException 中上報的很多異常,業務明明是處理了的,但還是被捕獲到了。

Task 中的異常

在 C# 中,寫這樣一段程式碼:

Task.Run(() =>
{
    throw new InvalidOperationException("throw in task");
});

這裡異常在 Task 裡面沒有被捕獲,外面也沒有 await Task 的執行結果,那麼,這個異常就成了遊離的狀態。
如果訂閱了 TaskScheduler.UnobservedTaskException 事件,TaskScheduler.UnobservedTaskException += TaskSchedulerOnUnobservedTaskException


在垃圾回收時,如果發現有 Task 中的異常沒有被處理,則會在這個事件中暴露。

不訂閱這個事件也不影響,從 .NET45 開始,這個異常不會導致程序意外終止。只是從業務上,會不知道發生了什麼。

TaskCompletionSource 中的異常

如果使用了 TaskCompletionSource,使用了類似這樣的程式碼,

var resultCompletionSource = new TaskCompletionSource<T>();

try
{
    var result = await Runner.Do();
    resultCompletionSource.SetResult(result);
    return result;
}
catch (Exception ex)
{
    resultCompletionSource.SetException(ex);  // 給 TaskCompletionSource 設定異常
    throw;
}

因為一些業務邏輯,最終 resultCompletionSource 沒有被使用(被 await 拿結果),如果 resultCompletionSource 被設定了異常,則在垃圾回收 resultCompletionSource 時,也會觸發 TaskScheduler.UnobservedTaskException 事件(如果訂閱了)。

所以,需要找一個合適的時機,主動捕獲 resultCompletionSource 中的異常。

try
{
    if (resultCompletionSource.Task.IsFaulted)
    {
        await resultCompletionSource.Task;
    }
}
catch (Exception e)
{
    Console.WriteLine(e);
}

背景問題原因

其實就是,雖然業務上處理了異常,但是有被“遺棄”的 TaskCompletionSource 中還有異常,最後垃圾回收時被 TaskScheduler.UnobservedTaskException 發現了。

作者: J.晒太陽的貓 出處: https://www.cnblogs.com/jasongrass/ 本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連結,否則保留追究法律責任的權利。