2020年9月26日 星期六

在執行緒內拋出例外異常,可否捕捉到

在執行緒內拋出例外異常,可否捕捉到

許多人在使用 C# 非同步程式設計的 TAP 模式時候,往往會聽到這樣的強調內容:當設計 非同步方法,也就是在方法前面加上 async 修飾詞,而該方法的回傳值需要為 Task 或者 Task<T>,千萬不要把這個非同步方法的回傳值宣告為 void,因為,這樣的宣告方式,僅適用於有事件訂閱的非同步委派方法內使用。

可是,往往大家在設計非同步方法的時候,都僅會在非同步方法前面加入 async 修飾詞,而將該非同步方法的回傳值宣告為 void;就是因為回傳值為 void,因此,當要呼叫這個非同步方法的時候,就僅能夠直接呼叫該非同步方法,而不能使用 await 關鍵字 (這是因為該非同步方法的回傳值必須是一個 Task 型別的物件,因為,await 之後,要接著一個 Task 物件,這樣才能等待)。

接著,更可怕的事情就發生了,因為這樣的呼叫方式,造成了射後不理的情況,也因為如此,也就無法使用 try...catch 敘述來捕捉這樣的非同步方法的例外異常。為了要讓大家更清楚問題在哪裡,因此,底下的程式碼將會使用執行緒來說明。

首先,在主執行緒內,故意拋出例外異常 Exception,並且使用 try ... catch 敘述來捕捉這個例外異常,這樣的寫法是標準作法,理所當然會捕捉到例外異常,而不會造成處理程序崩潰;不過,接下來使用 try...catch 將建立一個執行緒,等候三秒鐘之後,拋出例外異常,最後啟動該執行緒這樣的過程都包起來,因此,在程式執行後的三秒鐘後,並無法成功攔截捕捉到例外異常,而是造成 Process 崩潰,也就是說,在該執行緒的外面,是無法使用 try...catch 敘述來捕捉到該執行緒內所產生的任何例外異常錯誤。

static void Main(string[] args)
{
    Console.WriteLine($"Main Thread Id :" +
       $"{Thread.CurrentThread.ManagedThreadId}");
    try
    {
        throw new Exception("Capture Main Exception");
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
 
    try
    {
        Thread thread = new Thread(() =>
        {
            Console.WriteLine($"New Thread Id :" +
                $"{Thread.CurrentThread.ManagedThreadId}");
            Thread.Sleep(3000);
            throw new Exception("Oh Oh");
        });
        thread.Start();
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
 
 
}


 

沒有留言:

張貼留言