2013-03-06 92 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