【问题标题】:Is Task.Delay Worth Cancellation?Task.Delay 值得取消吗?
【发布时间】:2015-12-02 07:37:02
【问题描述】:

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

在我的例子中,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 对我来说不是正确的选择。 感觉好像我把它弄得比我应该做的更复杂。任何关于这种情况的线索将不胜感激。

【问题讨论】:

    标签: c# .net wcf async-await task-parallel-library


    【解决方案1】:

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

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

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

    【讨论】:

    • 你的意思是一个未取消的待处理的Task.Delay任务,并且没有附加到它的延续,可以延迟应用程序的关闭?
    • 仅当任务已创建为 IRegisteredObject 时,该任务完成后才被释放。如果您正在构建一个类库,您可能不知道您的任务是如何创建的,因此最好在令牌请求后取消所有延迟
    • Alex 在他们的GetOrTimeout 方法中使用Task.Delay 在内部作为超时机制。此任务不通过 API 公开。 GetOrTimeout 返回的任务仅在 Task&lt;T&gt; task 参数运行时观察内部 Task.Delay,然后与它分离。您的回答是否与 OP 的场景或 Task.Delay 的其他可能用途有关?
    • @TheodorZoulias OP 等待任务。这发生在私有方法中,但我们不知道该私有方法是否没有通过某个地方的另一个公共方法公开,并且可以使用“注册”调用从 Web 应用程序调用该公共方法(阻止应用程序的调用)关闭直到完成)。无论如何,我正在回答一个更普遍的问题 - “延迟值得取消” - 是的,在网络应用程序中。希望这对某人有用。
    • 假设由 OP 实现的 GetOrTimeout 方法由 Web 应用程序注册。您认为注释掉cts.Cancel(); 语句可能会产生延迟Web 应用程序关闭的效果吗?很抱歉坚持,但我认为澄清这一点很重要。 OP 可能会在阅读您的回答后感到担忧,如果他们的担忧被证明是不合理的,那将是很遗憾的。
    【解决方案2】:

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

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

    如果您“大量”使用Task.Delay,您可能会在有用后很长时间内浪费大量资源。如果您还使用捕获任何引用的委托向 Task.Delay 任务添加任何延续,它们也会无缘无故地徘徊。

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

    【讨论】:

    • @AlexeiLevenkov 为什么你认为这就是它的作用?
    • 你是正确的取消传递给Task.Delay的令牌配置计时器。我认为它的行为方式与常规任务检查取消的方式相同,但事实并非如此。
    • @i3arnon,谢谢。是的 - 我正在寻找一种可以应用很多的模式。我希望你能看到这个问题,因为我之前在你的一个答案中看到了空洞的延续。如果不是最佳实践,这种超时任务方法似乎正在成为common proactice,我想知道您认为这是好是坏。
    • @Clay 使用延迟任务超时是大多数时候你能做的最好的事情。如果可以的话,我也更喜欢处理实例 (but people seem to disagree)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-10-14
    • 1970-01-01
    • 2014-07-15
    • 2015-01-04
    • 1970-01-01
    • 1970-01-01
    • 2015-08-24
    相关资源
    最近更新 更多