2019年3月1日 星期五

等待 wait 工作 Task 物件的非同步方法在不同時間點拋出例外異常

等待 wait 工作 Task 物件的非同步方法在不同時間點拋出例外異常

當我們建立一個非同步工作的時候,這個非同步工作將有機會在不同的時間點拋出例外異常,此時,若我們使用了 Task.Wait() 方法來等待這個工作完成,那麼會有甚麼情況發生了?例如,非同步工作尚未開始之前,就直接拋出例外異常、在非同步方法執行期間,但是在工作等待前,就在非同步方法內拋出例外異常,此時當在等待此工作的時候,會有甚麼結果、最後,當非同步方法處理時間較長,而主執行緒早早就在等待此工作的完成,此時,非同步工作拋出例外異常,整個應用程式會發生甚麼問題
為了解答這些疑問,我們將要建立這樣的非同步方法,在主執行緒中,當啟動這個非同步方法之後,將會休息 1000 ms,而在非同步方法內,將會有底下幾個時間點拋出例外異常 Exception
  • 呼叫非同步方法的時候且尚未執行非同步任何程式碼的時候,立即拋出例外異常
  • 非同步方法執行 500ms 之後,拋出例外異常
  • 非同步方法執行 2000ms 之後,拋出例外異常

呼叫非同步方法的時候且尚未執行非同步任何程式碼的時候,立即拋出例外異常

我們建立一個 MyAsync 非同步方法,該非同步方法將會回傳一個工作 Task 物件,此時,在主執行緒內,將會休息 1000 ms 的時間,接者,使用 Wait() 方法等待 MyAsync 非同步方法的完成。
此時,該 MyAsync 非同步方法將會立即使用 Task.FromException 回傳一個工作物件,且該工作處於一個拋出例外異常的狀態,也就是該工作的 IsFaulted = true
C Sharp / C#
class Program
{
    static void Main(string[] args)
    {
        Task task = MyAsync();
        Thread.Sleep(1000);
        task.Wait();
        Console.WriteLine("Press any key for continuing...");
        Console.ReadKey();
    }
    static Task MyAsync()
    {
        return Task.FromException(new Exception("非同步工作尚未開始,模擬拋出例外異常"));
    }
}
當執行上述範例程式,一旦當主執行緒休息 1000ms 後,接著使用 Wait() 方法等候該非同步方法完成的時候,將會得到底下的例外異常:非同步工作尚未開始,模擬拋出例外異常。
當然,該應用程式將會異常終止結束執行
Console
System.AggregateException
  HResult=0x80131500
  Message=One or more errors occurred.
  Source=System.Private.CoreLib
  StackTrace: 
   at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
   at System.Threading.Tasks.Task.Wait()
   at ConsoleApp2.Program.Main(String[] args) in D:\Vulcan\Projects\ConsoleApp2\ConsoleApp2\Program.cs:line 14

內部例外狀況 1:
Exception: 非同步工作尚未開始,模擬拋出例外異常

非同步方法執行 500ms 之後,拋出例外異常

我們建立一個 MyAsync 非同步方法,該非同步方法將會回傳一個工作 Task 物件,此時,在主執行緒內,將會休息 1000 ms 的時間,接者,使用 Wait() 方法等待 MyAsync 非同步方法的完成。
此時,該 MyAsync 非同步方法將會進入到非同步處理狀態,這裡我們使用的是 Task.Run 來取得執行緒集區內的一個執行緒,進行多工處理;而在該新執行緒內,將會休息 500ms (使用 Thread.Sleep(500) 方法) 接著拋出例外異常 (使用 SetException 方法)
C Sharp / C#
class Program
{
    static void Main(string[] args)
    {
        Task task = MyAsync();
        Thread.Sleep(1000);
        task.Wait();
        Console.WriteLine("Press any key for continuing...");
        Console.ReadKey();
    }
    static Task MyAsync()
    {
        TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
        Task.Run(() =>
        {
            Thread.Sleep(500);
            tcs.SetException(new Exception("非同步工作開始,在等待工作前,模擬拋出例外異常"));
        });
        return tcs.Task;
    }
}
當執行上述範例程式,一旦當主執行緒休息 1000ms 後,接著使用 Wait() 方法等候該非同步方法完成的時候,此時主執行緒將會進入到封鎖 Block 狀態下,也就是主執行緒在 1000ms 之前,是無法處理任何事情的,而在非同步方法內,將會使用 Task.Run 進行非同步處理,並且在 500ms 之後,使用 TaskCompletionSource.SetException 方法,設定該非同步工作有例外異常產生,此時,將會得到底下的例外異常:非同步工作開始,在等待工作前,模擬拋出例外異常。
當然,該應用程式將會異常終止結束執行
Console
System.AggregateException
  HResult=0x80131500
  Message=One or more errors occurred.
  Source=System.Private.CoreLib
  StackTrace: 
   at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
   at System.Threading.Tasks.Task.Wait()
   at ConsoleApp2.Program.Main(String[] args) in D:\Vulcan\Projects\ConsoleApp2\ConsoleApp2\Program.cs:line 14

內部例外狀況 1:
Exception: 非同步工作開始,在等待工作前,模擬拋出例外異常

非同步方法執行 2000ms 之後,拋出例外異常

我們建立一個 MyAsync 非同步方法,該非同步方法將會回傳一個工作 Task 物件,此時,在主執行緒內,將會休息 1000 ms 的時間,接者,使用 Wait() 方法等待 MyAsync 非同步方法的完成。
此時,該 MyAsync 非同步方法將會進入到非同步處理狀態,這裡我們使用的是 Task.Run 來取得執行緒集區內的一個執行緒,進行多工處理;而在該新執行緒內,將會休息 2000ms (使用 Thread.Sleep(2000) 方法) 接著拋出例外異常 (使用 SetException 方法)
C Sharp / C#
class Program
{
    static void Main(string[] args)
    {
        Task task = MyAsync();
        Thread.Sleep(1000);
        task.Wait();
        Console.WriteLine("Press any key for continuing...");
        Console.ReadKey();
    }
    static Task MyAsync()
    {
        TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
        Task.Run(() =>
        {
            Thread.Sleep(2000);
            tcs.SetException(new Exception("非同步工作開始,在等待工作執行之後,才模擬拋出例外異常"));
        });
        return tcs.Task;
    }
}
當執行上述範例程式,一旦當主執行緒休息 1000ms 後,接著使用 Wait() 方法等候該非同步方法完成的時候,此時主執行緒將會進入到封鎖 Block 狀態下,也就是主執行緒在 1000ms 之前,是無法處理任何事情的,而在非同步方法內,將會使用 Task.Run 進行非同步處理,並且在 2000ms 之後,使用 TaskCompletionSource.SetException 方法,設定該非同步工作有例外異常產生,這裡是要模擬當主執行緒在等待非同步方法完成,不過,非同步方法需要就久的時間才能夠完成處理的情境,此時,將會得到底下的例外異常:非同步工作開始,在等待工作執行之後,才模擬拋出例外異常。
+

當然,該應用程式將會異常終止結束執行
Console
System.AggregateException
  HResult=0x80131500
  Message=One or more errors occurred.
  Source=System.Private.CoreLib
  StackTrace: 
   at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
   at System.Threading.Tasks.Task.Wait()
   at ConsoleApp2.Program.Main(String[] args) in D:\Vulcan\Projects\ConsoleApp2\ConsoleApp2\Program.cs:line 14

內部例外狀況 1:
Exception: 非同步工作開始,在等待工作執行之後,才模擬拋出例外異常

結論

當使用工作類別來設計出非同步方法的時候,不論該非同步方法在甚麼時間點拋出例外異常,只要呼叫該非同步方法的執行緒使用 Wait 方法來等待該工作,這個時候,此應用程式將會終止執行。