2013-03-06 95 views
2

問題關聯起來的CancellationToken:有沒有辦法與從async方法返回的Task一個CancellationToken關聯?與異步方法的任務

一般地,如果OperationCancelledExceptionCancellationToken匹配TaskCancellationToken拋出Task將在取消狀態結束。如果它們不匹配,則任務進入斷陷狀態:

void WrongCancellationTokenCausesFault() 
    { 
     var cts1 = new CancellationTokenSource(); 
     var cts2 = new CancellationTokenSource(); 
     cts2.Cancel(); 

     // This task will end up in the Faulted state due to the task's CancellationToken not matching the thrown 
     // OperationCanceledException's token. 
     var task = Task.Run(() => cts2.Token.ThrowIfCancellationRequested(), cts1.Token); 
    } 

隨着async/await,我還沒有找到一個方法來設置方法的TaskCancellationToken(進而達到相同類型的功能)。從我的測試,似乎任何OperationCancelledException將導致async方法進入已取消狀態:

async Task AsyncMethodWithCancellation(CancellationToken ct) 
    { 
     // If ct is cancelled, this will cause the returned Task to be in the Cancelled state 
     ct.ThrowIfCancellationRequested(); 
     await Task.Delay(1); 

     // This will cause the returned Task to be in the Cancelled state 
     var newCts = new CancellationTokenSource(); 
     newCts.Cancel(); 
     newCts.Token.ThrowIfCancellationRequested(); 
    } 

這將是很好,有一點更多的控制,因爲如果一個方法,我從我的async方法調用被取消(我不希望取消 - 即它不是TaskCancellationToken),我希望任務進入Faulted狀態 - 而不是取消狀態。

回答

2

我認爲這種設計對於常見的情況非常有效:如果有任何子操作被取消,則取消會傳播給父級(最常見的情況是父級和子級共享取消標記)。

如果你想要不同的語義,你可以在你的async方法中使用catchOperationCanceledException並拋出一個符合你需要的語義的異常。如果你想重複使用這些語義,Task的擴展方法應該適合賬單。

+0

我同意 - 它適用於常見情況。有點奇怪的是,一個小孩'Task'(非'async')可能最終出錯(由於OperationCancelledException帶有錯誤的標記),但是這樣會導致父代('async')Task處於取消狀態。大多數情況下,我只是好奇並擔心bug的情況:意外的取消不會導致我的'async'代碼失敗,並且可能會延遲我注意到這個錯誤。 – 2013-03-06 21:43:07

+0

有一些角落案件重新取消;例如,如果您將一個已經取消的標記傳遞給Task.Run,​​您將會得到一個取消的任務,在await時引發'TaskCanceledException',但是如果您取消了傳遞給Task.Run後的標記,任務開始,你會得到一個取消的任務,當await被引發時引發'OperationCanceledException'(* not *'TaskCanceledException')。無論如何,你不應該擔心錯誤,因爲你最終應該「等待」所有的任務(你關心的),任何被取消的任務將會在等待時拋出'OperationCanceledException'。 – 2013-03-06 21:54:45