取消令牌的任务?

nos*_*tio 16 .net c# multithreading task-parallel-library async-await

鉴于取消令牌,我想创建一个等待它的任务,这是永远不会完成但可以取消.我需要这样的模式,IMO应该很常见:

async Task DoStuff(Task t, CancellationToken ct)
{
   // t was made from TaskCompletionSource, 
   // both t and ct are beyond my control

   Task t2 = TaskFromCancellationToken(ct);
   await Task.WhenAny(t, t2);

   // do stuff
}
Run Code Online (Sandbox Code Playgroud)

我到目前为止最好的想法是:

Task TaskFromCancelationToken(CancellationToken ct)
{
    return Task.Delay(Timeout.Infinite, ct);
}
Run Code Online (Sandbox Code Playgroud)

是否有更好的方法来实现这种逻辑?

Ste*_*ary 21

它并不是非常常见,但它很常见,成为我的AsyncEx库的一部分.我使用这样的东西:

public static Task AsTask(this CancellationToken cancellationToken)
{
    var tcs = new TaskCompletionSource<object>();
    cancellationToken.Register(() => tcs.TrySetCanceled(),
        useSynchronizationContext: false);
    return tcs.Task;
}
Run Code Online (Sandbox Code Playgroud)

  • 但是 [`cancellationToken.Register`](https://msdn.microsoft.com/en-us/library/dd321635(v=vs.110).aspx) 返回 [`CancellationTokenRegistration`](https://msdn.microsoft .com/en-us/library/system.threading.cancellationtokenregistration(v=vs.110).aspx),你永远不会处理它(它实现了`IDisposable`)。那不是问题吗? (2认同)
  • @Paya为了不泄漏注册,您所要做的就是处置`CancellationTokenSource`,它将处置其所有令牌的注册。 (2认同)
  • @StriplingWarrior:是的。在我写这个答案的时候,这种过载并不存在。[现代 AsyncEx 代码](https://github.com/StephenCleary/AsyncEx/blob/edb2c6b66d41471008a56e4098f9670b5143617e/src/Nito.AsyncEx.Tasks/CancellationTokenTaskSource.cs#L29) 确实通过了 (2认同)

use*_*932 10

Task.Delay(Timeout.Infinite, cancellationToken)
Run Code Online (Sandbox Code Playgroud)

您在问题中提出的答案是我所知道的最佳解决方案.原因如下:

  • 这很安全
  • 需要非常小的代码
  • 标准图书馆

根据Task.Delay我的知识,这种方法被很多人大量使用,并且也推荐在微软博客上使用.MSDN示例.

为什么要自己编写代码(包括测试)来利用TaskCompletionSource将取消令牌转换为任务?最好是利用标准库而不是重新发明轮子; 它们比你的代码更容易出错.

  • 请注意,完成后此调用始终会引发“OperationCanceledException”,请参阅此处的文档:https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task.delay (3认同)
  • 当cancelToken转换为已取消状态时,将引发“OperationCanceledException” (2认同)