2014-08-28 149 views
1

我有以下代碼,其中Task可以被取消,但我基本上需要等待它完成(以確保完整性),然後將OperationCanceledException扔給調用者。等待取消的任務在繼續執行之前完成

public static void TaskCancellationTest() { 
    try { 
     Console.WriteLine("TaskCancellationTest started."); 
     var cts = new CancellationTokenSource(); 
     var t = Task.Run(() => { 
      if (cts.Token.IsCancellationRequested) return; 
      Console.WriteLine("1"); 
      Task.Delay(2000).Wait(); 
      Console.WriteLine("2"); 
     }).ContinueWith(task => { 
      if (cts.Token.IsCancellationRequested) return; 
      Console.WriteLine("3"); 
      Task.Delay(2000).Wait(); 
      Console.WriteLine("4"); 
     }); 
     Task.Run(() => { 
      Task.Delay(1000).Wait(); 
      Console.WriteLine("Cancelling..."); 
      cts.Cancel(); 
     }); 
     t.Wait(); 
     try { 
      cts.Token.ThrowIfCancellationRequested(); 
     } 
     catch (OperationCanceledException) { 
      Console.WriteLine("Gracefully canceled."); 
     } 
     Console.WriteLine("TaskCancellationTest completed."); 
    } 
    catch (Exception ex) { 
     Console.WriteLine("TaskCancellationTest... Failure: " + ex); 
    } 
} 

結果如市場預期,是:

1 
Cancelling... 
2 
Gracefully canceled. 

它的工作原理,但我寧願到CancellationToken傳遞給方法,因爲我明白這是一個更好的模式。我還希望能夠觀察方法體內的令牌,並呼叫ThrowIfCancellationRequested()放棄而不必等待下一個ContinueWith()。

我正在玩下面的替代代碼,這也適用,但有沒有辦法讓OperationCanceledException而不是AggregateException

如果我通過的CancellationToken到WaitAll()方法,問題是,它會在令牌的消除立即拋出OperationCanceledException,而不是等待的任務t1t2實際完成(他們將繼續在後臺運行)然後只拋出異常。

public static void TaskCancellationTest2() { 
    try { 
     Console.WriteLine("TaskCancellationTest2 started."); 
     var cts = new CancellationTokenSource(); 
     var t1 = Task.Run(() => { 
      Console.WriteLine("1"); 
      Task.Delay(2000).Wait(); 
      Console.WriteLine("2"); 
     }, cts.Token); 
     var t2 = t1.ContinueWith(task => { 
      Console.WriteLine("3"); 
      Task.Delay(2000).Wait(); 
      cts.Token.ThrowIfCancellationRequested(); 
      Console.WriteLine("4"); 
     }, cts.Token); 
     Task.Run(() => { 
      Task.Delay(1000).Wait(); 
      Console.WriteLine("Cancelling..."); 
      cts.Cancel(); 
     }); 
     try { 
      try { 
       Task.WaitAll(t1, t2); 
      } 
      catch (AggregateException ae) { 
       if (ae.InnerExceptions.Count == 1 && ae.InnerExceptions.Single() is OperationCanceledException) { 
        throw ae.InnerExceptions.Single(); 
       } 
       throw; 
      } 
     } 
     catch (OperationCanceledException) { 
      Console.WriteLine("Gracefully canceled."); 
     } 
     Console.WriteLine("TaskCancellationTest2 completed."); 
    } 
    catch (Exception ex) { 
     Console.WriteLine("TaskCancellationTest2... Failure: " + ex); 
    } 
} 

我準備了一把小提琴here

This question的標題與我的非常相似,但是接受的答案不幸與我的情況無關。

你知道有什麼方法可以達到我想要的效果嗎?儘量使用CancellationToken

回答

1

我認爲如果設置了CancellationToken,TPL就是專門用來完成任務的。部分原因是您看到此行爲是因爲您致電t.Wait(cts.Token)。即使任務沒有運行到完成,如果令牌被設置,需要CancellationToken的超載也將停止等待。

ContinueWith一樣,如果您傳遞CancellationToken任務可以在設置該令牌後立即完成。

將代碼更改爲不帶令牌的t.Wait()ContinueWith,您將獲得所需的行爲。

public static void TaskCancellationTestNotWorking1() 
    { 
     try 
     { 
      Console.WriteLine("TaskCancellationTestNotWorking started."); 
      var cts = new CancellationTokenSource(); 
      var t = Task.Run(() => 
      { 
       Console.WriteLine("1"); 
       Thread.Sleep(2000); 
       Console.WriteLine("2"); 
      }, cts.Token).ContinueWith(task => 
      { 
       Console.WriteLine("3"); 
       Thread.Sleep(2000); 
       cts.Token.ThrowIfCancellationRequested(); 
       Console.WriteLine("4"); 
      }); 

      Task.Run(() => 
      { 
       Thread.Sleep(1000); 
       Console.WriteLine("Cancelling..."); 
       cts.Cancel(); 
      }, cts.Token); 

      try 
      { 
       t.Wait(); 
      } 
      catch (OperationCanceledException) 
      { 
       Console.WriteLine("IsCanceled " + t.IsCanceled); 
       Console.WriteLine("IsCompleted " + t.IsCompleted);      

       Console.WriteLine("Gracefully canceled."); 
      } 
      catch (AggregateException) 
      { 
       Console.WriteLine("IsCanceled " + t.IsCanceled); 
       Console.WriteLine("IsCompleted " + t.IsCompleted); 

       Console.WriteLine("Gracefully canceled 1."); 
      } 

      Console.WriteLine("TaskCancellationTestNotWorking completed."); 
     } 
     catch (Exception ex) 
     { 
      Console.WriteLine("TaskCancellationTestNotWorking... Failure: " + ex); 
     } 
    } 

您可能會發現這篇文章有用How do I cancel non-cancelable async operations?

+0

如果我不叫't.Wait()','然後Console.WriteLine( 「2」);'會後'TaskCancellationTestNotWorking異步執行( )'會返回。這是我需要避免的(如果我正在編寫文件而不是寫入「2」,則需要確保TaskCancellationTestNotWorking()不會在文件寫入操作完成之前返回)。 – 2014-08-28 04:49:27

+0

如果任務處於「已取消」狀態,則「IsCompleted」將返回true,儘管它仍可能正在執行代碼。 – 2014-08-28 04:51:12

+0

你已經調用了'Wait()'並且它拋出了一個異常,你爲什麼要再次調用它? – 2014-08-28 06:08:24