2013-03-25 112 views
4

昨天我剛剛介紹了任務(TPL),因此我試着做一個小樣本項目以瞭解如何使用它們。任務取消和TaskContinuationOptions

我的示例項目設置了一個開始按鈕,開始遞增進度條。第二個按鈕取消任務。一個文本框,用於報告何時使用TaskContinuationOptions.OnlyOnRanToCompletion進行的延續以及用於報告何時使用TaskContinuationOptions.OnlyOnCanceled進行延續的文本框。

我可以創建並執行一個任務,但取消它的方式可以讓TaskContinuationOptions.OnlyOnCanceled標誌繼續觸發,這是一個問題。

我創建的任務如下:

private void StartTask() 
{ 
    CancellationTokenSource tokenSource = new CancellationTokenSource(); 
    CancellationToken token = tokenSource.Token; 

    Task task = null; 
    task = Task.Factory.StartNew(() => DoWork(tokenSource), tokenSource.Token); 

    //A list<CancellationTokenSource> so that I can cancel the task when clicking a button on the UI Thread. 
    MyTasks.Add(tokenSource); 

    Task completed = task.ContinueWith(result => TaskCompleted(), TaskContinuationOptions.OnlyOnRanToCompletion); 
    Task canceled = task.ContinueWith(result => TaskCanceled(), TaskContinuationOptions.OnlyOnCanceled); 
} 

我取消任務如下:

private void CancelTasks() 
{ 
    foreach (CancellationTokenSource tokenSource in MyTasks) 
    { 
     tokenSource.Cancel();     
    } 
} 

我的工人功能如下:

private void DoWork(CancellationTokenSource tokenSource) 
{ 
    if (progressBar1.InvokeRequired) 
    { 
     progressBar1.Invoke(new Action(() => DoWork(tokenSource))); 
     return; 
    } 

    try 
    { 
     bool dowork = true; 
     while (dowork) 
     { 
      tokenSource.Token.ThrowIfCancellationRequested(); 

      if (progressBar1.Value == progressBar1.Maximum) 
      { 
       dowork = false; 
      } 
      Thread.Sleep(1000); 
      progressBar1.PerformStep(); 
      Application.DoEvents(); 
     } 
     countCompleted++; 
    } 
    catch (OperationCanceledException) 
    {     
    } 
} 

在其他職位,我已閱讀,它已被建議tokenSource.Token.ThrowIfCancellationRequested()是什麼設置條件評估由TaskContinuationOptions.OnlyOnCanceled編輯。

無的我所看到的實施例包括使用的:

catch (OperationCanceledException) 
{ 
} 

然而,如果沒有它的程序停止時,我調用tokenSource.Cancel();

就目前而言,當我調用tokenSource.Cancel()時,將繼續使用TaskContinuationOptions.OnlyOnRanToCompletion運行,而不是TaskContinuationOptions.OnlyOnCanceled。

很明顯,我沒有正確地做到這一點。

編輯

做一些進一步的閱讀中,我發現,規定的評價是:

「趕上(OperationCanceledException){}將設置爲RanToCompletion任務狀態,而不是取消」

因此刪除catch(OperationCanceledException){}允許將任務的狀態設置爲取消,但是程序在tokenSource.Token.ThrowIfCancellationRequested()中斷。但是如果我繼續完成中斷,那麼運行TaskContinuationOptions.OnlyOnCanceled的延續任務會很好。

但我該如何調用tokenSource.Token.ThrowIfCancellationRequested()而不允許程序中斷,同時允許將任務狀態設置爲取消?

+1

而不是調用ThrowIfCancellationRequested(),檢查IsCancellationRequested屬性並打破循環? – Jon 2013-03-25 01:41:12

+0

@Jon當我嘗試這樣做時,它確實允許我跳出循環,但任務的狀態將返回爲RanToCompletion,而不是取消,因此會調用錯誤的延續。 – Blau 2013-03-25 01:54:29

+0

好吧,我發現另一個帖子,有人遇到類似的問題;他們表示他們意識到他們正在以調試模式啓動該程序,並且在未經調試的情況下運行該程序時就能正常工作。所以我嘗試了這一點,並且一切看起來都正常。但是這意味着我不能在調試模式下運行而不必處理這個拋出的異常。有沒有辦法在調試模式下運行時正常工作? – Blau 2013-03-25 03:01:47

回答

9

上述註釋在調試器和防止調試器中斷所需的選項方面是正確的。但是,下面應該給你一個更好的例子,說明如何使用延續,以及如何處理這些延續中的任務拋出的異常。

延續可以發現異常是否由先行任務的異常屬性引起的先行Task。如果task1拋出一個異常,而這個異常沒有捕獲/繼續問,它被認爲是未處理和應用模具下打印一個NullReferenceException結果到控制檯

Task task1 = Task.Factory.StartNew (() => { throw null; }); 
Task task2 = task1.ContinueWith (ant => Console.Write(ant.Exception()); 

。有了延續它足以通過Status關鍵字建立任務的結果

asyncTask.ContinueWith(task => 
{ 
    // Check task status. 
    switch (task.Status) 
    { 
     // Handle any exceptions to prevent UnobservedTaskException.    
     case TaskStatus.RanToCompletion: 
      if (asyncTask.Result) 
      { 
       // Do stuff... 
      } 
      break; 
     case TaskStatus.Faulted: 
      if (task.Exception != null) 
       mainForm.progressRightLabelText = task.Exception.InnerException.Message; 
      else 
       mainForm.progressRightLabelText = "Operation failed!"; 
     default: 
      break; 
    } 
} 

如果你不使用延續你要麼必須在任務等待在try/catch塊或查詢任務的Result在一個try/catch

int x = 0; 
Task<int> task = Task.Factory.StartNew (() => 7/x); 
try 
{ 
    task.Wait(); 
    // OR. 
    int result = task.Result; 
} 
catch (AggregateException aggEx) 
{ 
    Console.WriteLine(aggEx.InnerException.Message); 
} 

希望這有助於。

+0

最後一個catch(AggregateException aggEx)應該處理多個異常,並用類似 的foreach(異常innerEx在aggEx.Flatten()中嵌套AggregateExceptions){Console.WriteLine(innerEx.Message); } – 2015-07-27 19:44:04