2013年12月27日 星期五

[W8App][C#] Task Cancellation

若想要讓產生的Task具有可取消的機制,則必需透過以下static methods取得task handle與傳入cancel token來達成
Task.Run(Action, CancellationToken)
Task.Run(Func<Task>, CancellationToken)
Task.Run<TResult>(Func<Task<TResult>>, CancellationToken)
Task.Run<TResult>(Func<TResult>, CancellationToken)

這個範例是我設計的一個對話盒控制項,當對話盒產生時呼叫InitItemsAsync以非同步的方式產生圖片對載入UI的list控制項中,使用者可以不必等待所有的圖片都產生即按下OK(或Cancel)按來關閉這個對話盒。因此我必需讓InitItemsAsync具有可被取消的機制。

   1: public sealed partial class MyDialog : UserControl
   2: {
   3:     private CancellationTokenSource _cts;
   4:     private CancellationToken _token; 
   5:     private Task _opInit;

#3-5 我們的物件必需儲存CancellationTokenSource、CancellationToken與Task方便在動作的對應函式中取消



   1: private async Task InitItemsAsync()
   2: {
   3:     for(int i=0 ; i<24 ; i++)
   4:     {
   5:         if (_token.IsCancellationRequested) break;
   6:  
   7:         await LoadImageAsync(i);
   8:     }
   9: }

#5 非同步的InitItemsAsync函式,當_token收到取消的要求時(IsCancellationRequested)提前結束迴圈



   1: _cts = new CancellationTokenSource();
   2: _token = _cts.Token;
   3: _opInit = Task.Run(async () =>
   4: {
   5:     await InitItemsAsync();
   6: },
   7: _token);

透過Task.Run來執行非同步的函式,並載入cancel token。
#5 InitItemAsync是非同步的函式,因此必需有async / await來載明


PS: 我嘗試過不用Lambda的寫法,而是直接以函式指標的方式來使用Task.Run,但在VS2013編譯時期會有函式型別模糊(error CS0121: The call is ambiguous between the following methods or properties)的錯誤,目前還不知道解決的方式。



   1: private bool TryCancelInitItems()
   2: {
   3:     if (_opInit.IsCompleted == false)
   4:     {
   5:         try
   6:         {
   7:             _cts.Cancel();
   8:  
   9:             _opInit.Wait(_token);
  10:  
  11:             return true;
  12:         }
  13:         catch(System.OperationCanceledException)
  14:         {
  15:             return true;
  16:         }
  17:         catch (Exception e)
  18:         {
  19:             Debug.WriteLine(@"{0}", e);
  20:  
  21:             return false;
  22:         }
  23:     }
  24:  
  25:     return true;
  26: }
當使用者按下結束對話盒的按鈕時,呼叫我的TryCancelInitItems函式來取消InitItemsAsync。

#3 先檢查task是否已經完成,若尚未完成才需使用取消機制
#7 呼叫取消並等待_token
#13 若InitItemsAsync被取消了,會丟出System.OperationCanceledException