這節來講一下如果捕獲Task的異常。
當Task執行中出現了異常,正常情況下我們在主執行緒的Try是捕獲不到的,而如果在Task內部寫try,出現了異常我們會完全不知道。下面就來介紹幾個主執行緒捕獲Task異常的方法。
阻塞執行緒式
我們可以使用Wait(),WaitAny(),WaitAll()來捕獲Task的異常,詳見下圖:
捕獲Task異常,準確來說要用AggregateException類,右邊是執行結果,成功捕獲到了異常資訊,其它兩個等待也是類似的用法,不熟悉的小夥伴可以參見前文:等待多個非同步任務的方法。
在等待多個Task異常時,可以訪問異常物件的InnerExceptions屬性來遍歷所有的異常:
上述異常捕獲的解決方案,因為涉及到了等待,所以會阻塞主執行緒,並且如果異常發生在等待之前,同樣是不能捕獲到,所以這種方式,雖然簡單,但是使用場景並不多。
非同步式
我們知道Task有個ContinueWith方法,它會在Task完成後繼續非同步執行傳入的委託,我們可以通過這個方法實現異常捕獲,請看如下程式碼:
因為是非同步執行,所以這樣不會阻塞主執行緒。
事件式
事件式的思路是在主執行緒中定義事件,在Task中通過觸發事件的形式讓主執行緒捕獲到異常,請看程式碼:
首先定義一個事件引數:
internal class TaskExceptionEventArgs:EventArgs { /// <summary> /// 存放Task引發的異常物件 /// </summary> public AggregateException AggregateException { get; set; } }
主程式碼如下:
class Program { private static event EventHandler<TaskExceptionEventArgs> taskExceptionEventHandler; static void Main(string[] args) { //為事件新增事件處理器 taskExceptionEventHandler = (sender, aeArgs) => { Console.WriteLine(aeArgs.AggregateException.Message); }; Task.Run(async () => { await Task.Delay(2 * 1000); try { throw new AggregateException("內部異常1"); } catch (AggregateException ex) { //觸發事件,並傳入引數 taskExceptionEventHandler.Invoke(null, new TaskExceptionEventArgs { AggregateException = ex }); } }); } }
這樣用法很靈活,而且拿到的是最直接的異常物件,並且不用等待Task執行完畢。