這節來講一下如果捕獲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執行完畢。