2018年8月26日 星期日

TAP Task 產生例外異常 Exception 發生同步或者非同步的探討

TAP Task 產生例外異常 Exception 發生同步或者非同步的探討

我們知道,若直接使用執行緒 Thread 來設計非同步 Asynchronous 處理需求,我們是無法在啟動該執行緒那哩,透過 try { someThread.Star() } catch {...} 這樣的程式碼,捕捉到任何在該執行緒中發生的任何例外異常,您僅能夠在該執行緒方法內自行捕捉相關例外異常事件的產生。
而當我們使用 Task 類別協助我們進行非同步應用程式碼開發的時候,必定會遇到在非同步方法內會發生例外異常 Exception 的問題,而我們必須知道,當您使用 Task 進行設計的時候,在呼叫端是可以捕捉到這些非同步方法內產生的例外異常事件;可以這樣做到的原因在於當我們進行 Task 類別為基礎的非同步方法設計的時候,當有例外異常發生的話,當時發生的例外事件會儲存在 Task 物件內,我們可以透過 Task.Exception 這個物件來取得當時發生例外異常的物件。
不過,當進行 Task 非同步方法呼叫的時候,我們通常會使用 await 關鍵字來等候這個非同步工作完成,因此,當有使用 await 等候非同步工作的時候,若非同步方法內有例外異常產生,這個 await 將會在呼叫非同步方法的呼叫端,再次丟出這個例外異常出來,因此,我們就可以捕捉到。然而,若我們使用類似這樣的敘述 Task<string> task2 = GetContentAsync("https://www.google.com"); 與 var content = await task2;這兩個敘述的時候,會在呼叫非同方法的呼叫端產生例外異常的敘述將會是後者。
另外一個注意事項,那就是在 Task 為基礎的非同步方法內,在該非同步方法內尚未呼叫任何其他非同步方法的時候,例如,我們可能會在非同步工作方法的最前面,要檢查使用者是否有傳入適當的參數到這個非同步方法內,若沒有,我們就會丟出例外異常出來;不過,因為該非同步工作方法尚未進入呼叫與等候其他的非同步工作,因此,我們在呼叫端無需使用 await 關鍵字,當取得這個非同步工作的 Task 物件時候,就會產生了例外異常。我們以這兩行程式碼敘述為例 Task<string> task1 = GetContentAsync(null); 與 var content = await task1;,若符合剛剛敘述的情境,呼叫端的例外異常將會發生在前者敘述。想要測試這樣的過程,請將底下範例程式碼的 #region 在執行非同步方法時發生例外異常 到 #endregion 之間的敘述註解起來,並且在 Task<string> task1 = GetContentAsync(null); 與 Console.WriteLine(exceptionSync.Message); 這兩個敘述上設定一個除錯中斷點;現在,讓我們來實際執行這個範例程式碼。
當程式執行後,會立即停留在 Task<string> task1 = GetContentAsync(null); 方法上,此時,尚未進入到任何非同步工作方法內,現在,我們逐步執行來進行除錯,因為我們傳入一個空值到這個非同步工作內,因此,在這個非同步工作方法內在為呼叫讓和其他非同步工作的時候,就直接產生了例外異常,所以,Task<string> task1 = GetContentAsync(null); 這行敘述雖然僅是取得一個非同步工作 Task 物件,並且我們還沒有使用 await 關鍵字來等候這個非同步工作,就已經在呼叫端產生的例外異常。
現在,我們來看看另外一個情境,請將 #region 在執行非同步方法前發生例外異常 與 #endregion 之間的程式碼註解起來,並且將 #region 在執行非同步方法時發生例外異常 到 #endregion 之間的敘述解除註解,並且在 Task<string> task2 = GetContentAsync("https://www.google.com"); 與 Console.WriteLine(exceptionSync.Message); 這兩個敘述上設定一個除錯中斷點;讓我們來執行這段測試程式碼。
當程式執行後,程式就會停在 第一個中斷點 Task<string> task2 = GetContentAsync("https://www.google.com"); 上,現在我們繼續逐步執行這個除錯程式,我們發現到竟然沒有在呼叫端產生任何例外異常,這是因為當我們執行這個 GetContentAsync 非同步工作方法的時候,是在該方法內呼叫其他非同步工作的時候,才會產生例外異常,因此,我們程式碼可以繼續執行到這個敘述上 var content = await task2;。不過,當我們繼續執行等候這個非同步工作方法的時候,就發生了例外異常,這是因為此非同步工作的 Task 物件內 Exception 屬性上有例外異常物件,並且我們處於等候非同步工作情境下。
Task oject Exception Property
這篇文章的範例程式碼,可以從 https://github.com/vulcanlee/CSharpNotes2018/tree/master/AsyncTaskException 取得
C Sharp / C#
class Program
{
    static async Task Main(string[] args)
    {
        #region 在執行非同步方法前發生例外異常
        try
        {
            Task<string> task1 = GetContentAsync(null);
            var content = await task1;
        }
        catch (Exception exceptionSync)
        {
            Console.WriteLine(exceptionSync.Message);
        }
        #endregion

        #region 在執行非同步方法時發生例外異常
        try
        {
            Task<string> task2 = GetContentAsync("https://www.google.com");
            var content = await task2;
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
        #endregion


        Console.WriteLine("Press any key for continuing...");
        Console.ReadKey();

    }

    static Task<string> GetContentAsync(string url)
    {
        if (url == null)
            throw new ArgumentException($"{nameof(url)} 為空值");

        return GetContentInternalAsync(url);
    }

    static async Task<string> GetContentInternalAsync(string url)
    {
        var content = await new HttpClient().GetStringAsync(url);
        throw new HttpRequestException("模擬存取網路異常");
            return content;
        }
}

關於 Xamarin 在台灣的學習技術資源

Xamarin 實驗室 粉絲團
歡迎加入 Xamarin 實驗室 粉絲團,在這裡,將會經常性的貼出各種關於 Xamarin / Visual Studio / .NET 的相關消息、文章、技術開發等文件,讓您可以隨時掌握第一手的 Xamarin 方面消息。
Xamarin.Forms @ Taiwan
歡迎加入 Xamarin.Forms @ Taiwan,這是台灣的 Xamarin User Group,若您有任何關於 Xamarin / Visual Studio / .NET 上的問題,都可以在這裡來與各方高手來進行討論、交流。
+

Xamarin 實驗室 部落格
Xamarin 實驗室 部落格 是作者本身的部落格,這個部落格將會專注於 Xamarin 之跨平台 (Android / iOS / UWP) 方面的各類開技術探討、研究與分享的文章,最重要的是,它是全繁體中文。
Xamarin.Forms 系列課程
Xamarin.Forms 系列課程 想要快速進入到 Xamarin.Forms 的開發領域,學會各種 Xamarin.Forms 跨平台開發技術,例如:MVVM、Prism、Data Binding、各種 頁面 Page / 版面配置 Layout / 控制項 Control 的用法等等,千萬不要錯過這些 Xamarin.Forms 課程


沒有留言:

張貼留言