TaskScheduler.UnobservedTaskException 與 TaskCompletionSource 中的異常處理
阿新 • • 發佈:2021-08-11
背景問題
監控發現 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
發現了。