Task.Delay 值得取消吗?

Is Task.Delay Worth Cancellation?

我最近使用取消模式重新实现了一大堆异步 WCF 服务方法,我在很多地方都看到过这种模式 - 你等待一个 Task.WhenAny 开始的任务和一个 Task.Delay。当然,现有任务不可取消,但希望在以后的版本中解决。

在我的例子中,Task.Delay 的默认持续时间由服务设置控制。在绝大多数情况下,结果是希望的任务在规定的时间内完成。设置通常很慷慨。

我见过的大多数(但不是全部)例子都懒得取消Task.Delay。它是如此便宜以至于不值得担心吗?我知道取消会引发异常。如果我取消延迟,我是否应该处理异常?

这是我制作的所有服务方法都通过调用的方法:

private async Task<T> GetOrTimeout<T>( Task<T> task, [CallerMemberName] string caller = "" )
{
  using ( var cts = new CancellationTokenSource( ) )
  {
    try
    {
      var timeout = GetDelay( cts.Token );
      var first = await Task.WhenAny( task, timeout );
      if ( first == timeout ) throw new TimeoutException( Properties.Resources.TimeoutOccurredInService.Fmt( caller ) );
      cts.Cancel( );  //--> haven't been doing this. Should I?
      return await task;
    }
    catch ( Exception ex )
    {
      throw LoggedFaultException( ex, caller );
    }
  }
}

...创建延迟的方法如下所示:

private Task GetDelay( CancellationToken token )
{
  return Task
    .Delay( Properties.Settings.Default.ServiceMethodTimeout, token )
    .ContinueWith( _ => { }, TaskContinuationOptions.ExecuteSynchronously );
}

如果我不取消延迟,我是否会占用资源超过必要的时间?特别是,我担心 WCF 启动以调用服务方法的实例。我担心他们会削减服务配置的并发参数。超时设置非常粗略。不取消似乎很浪费,但这对我来说都是很新鲜的东西。

由于取消涉及异常,并且由于我接受过不使用异常来传达状态的培训,这感觉就像我将自己描绘成一些我不完全理解的可怕的反模式。也许 Task.Delay 对我来说不是正确的选择。它 感觉 就像我把它变得比我应该的更复杂。任何有关情况的线索都将不胜感激。

首先,整个问题在性能方面可能可以忽略不计,只有在真实环境中测试后才能考虑。

但是,如果我们深入研究,Task.Delay 会创建一个在特定时间间隔后完成的任务。它通过创建一个新的 System.Threading.Timer(实现 IDisposable)来实现,该 System.Threading.Timer 在间隔后使用 ThreadPool 线程完成承诺任务。

如果您使用 Task.Delay "a lot",您可能会浪费大量资源,而这些资源在有用之后仍会闲置很长时间。如果您还使用捕获任何引用的委托向 Task.Delay 任务添加任何延续,它们也会无缘无故地徘徊。

所以是的,取消任务比让它 运行 更安全,尽管可能不会太多。

Task.Delay当你关心你的应用程序的关闭速度时,值得取消。

一个例子是 asp.net 网络应用程序。

当服务器回收您的网络应用程序时(例如,当它正在实时更新时),它需要一切都快速结束。如果您有任务在后台等待,尤其是通过 QueueBackgroundObject 或类似技术注册的任务,应用可能需要一段时间才能关闭。