基於任務的異步編程模式(TAP)的錯誤處理 基於任務的異步編程模式(TAP)

在前面講到了《基於任務的異步編程模式(TAP)》,可是若是調用異步方法,沒有等待,那麼調用異步方法的線程中使用傳統的try/catch塊是不能捕獲到異步方法中的異常。由於在異步方法執行出現異常以前,已經執行完畢。html

一、沒有等待的調用異步方法

ThrowAfter方法是在必定延遲後拋出一個異常:編程

private async Task ThrowAfter(int ms,string message)
{
    await Task.Delay(ms);
    Console.WriteLine("異步任務隨後將拋出異常。");
    throw new Exception(message);
}

DontHandle方法在調用異步方法時,因爲有滯後性,因此使用try...catch...不能捕獲到異步方法中的異常。異步

public void DontHandle()
{
    try
    {
        ThrowAfter(200, "異步方法拋出的異常");
    }
    catch(Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
    Console.WriteLine("完成方法:DontHandle");
}

注意:返回void的異步方法不會等待。由於從async void方法拋出的異常沒法捕獲。所以,異步方法最好返回一個Task類型。

二、異步方法的異常處理

異步方法異常的比較好的處理方式使使用await關鍵字,將其放在try/catch語句中。async

public async void HandleOneError()
{
    Console.WriteLine("HandleOneError方法開始執行。。。");
    try
    {
        await ThrowAfter(2000, "異步方法拋出的異常");
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
    Console.WriteLine("完成方法:HandleOneError");
}

調用ThrowAfter方法後,HandleOneError會釋放線程,但它會在任務完成時保持對任務的引用。當異步方法拋出異常,會調用匹配的catch塊內的代碼。異步編程

三、多個異步方法的異常處理

若是調用多個異步方法,會有多個拋出異常,在捕獲異常時就會有問題。函數

public async void StartTwoTasks()
{
    Console.WriteLine("StartTwoTasks方法開始執行。。。");
    try
    {
        await ThrowAfter(2000, "first");//先執行該方法
        await ThrowAfter(1000, "Second");//第一個異步方法正常執行完後纔會執行該方法
    }
    catch(Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
    Console.WriteLine("完成方法:StartTwoTasks");
}

StartTwoTasks方法中,調用了兩個異步方法。理論上認爲,當第一個異步方法執行完,拋出異常後,緊接着就會調用第二個異步方法,並拋出異常。但其實是第一個異步方法拋出異常以後,就會被catch捕獲,並不會執行第二個異步方法。由於這種類型中,在「基於任務的異步編程模式(TAP)」一文中解釋過,這種調用方法是等待第一個異步方法執行結束後,調用函數的線程控制權纔會調用第二個異步方法,多個異步方法以此類推。可是當時咱們使用了Task類中的WhenAll方法同時等待多個任務所有執行完,才執行後面的代碼。post

public async void StartTwoTasksParallel()
{
    Console.WriteLine("StartTwoTasksParallel方法開始執行。。。");
    try
    {
        Task t1 = ThrowAfter(2000, "first");//先執行該方法
        Task t2 = ThrowAfter(1000, "Second");//第一個異步方法執行完後纔會執行該方法
        await Task.WhenAll(t1, t2);
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
    Console.WriteLine("完成方法:StartTwoTasksParallel");
}

StartTwoTasksParallel方法使用Task類的WhenAll方法,並行調用兩個不關聯的異步方法。該方法將等待全部任務結束後才結束調用,不論任何一個拋出異常都不會影響其餘任務。可是,該方法只會捕獲第一個異常(先拋出異常的任務),其餘異常將不會被顯示。url

有一種方法能夠獲取全部任務的異常信息,就是在try塊外聲明任務變量t1和t2,讓這兩個變量在catch塊內訪問。在catch塊中檢測任務的IsFaulted屬性確認任務的狀態,以斷定是否出現異常,而後經過Task類的Exception.InnerException訪問異常信息自己。spa

四、使用AggregateException信息

Task.WhenAll方法返回一個Task的結果變量。catch語句只會捕捉到全部異步任務中的第一個異常,可是Task.WhenAll方法返回的Task類型結果變量中會包含全部任務都出現的異常。外部結果任務的Exception屬性是一個AggregateException類型,顯示全部異常只須要遍歷結果任務中的Exception的InnerExceptions屬性便可。線程

public async void ShowAggregatedException()
{
    Console.WriteLine("ShowAggregatedException方法開始執行。。。");
    Task taskResult = null;
    try
    {
        Task t1 = ThrowAfter(2000, "first");//先執行該方法
        Task t2 = ThrowAfter(1000, "second");//第一個異步方法執行完後纔會執行該方法
        Task t3 = ThrowAfter(1500, "third");//第一個異步方法執行完後纔會執行該方法
        await (taskResult = Task.WhenAll(t1, t2, t3));
    }
    catch (Exception ex)
    {
        Console.WriteLine("handle {0}",ex.Message);
        foreach (Exception  ex1 in taskResult.Exception.InnerExceptions)
        {
            Console.WriteLine("Inner exception {0}", ex1.Message);
        }
    }
    Console.WriteLine("完成方法:ShowAggregatedException");
}
相關文章
相關標籤/搜索