【问题标题】:What is preferred in the while loop of the RunAsync method in a Service: use IsCancellationRequested or throw exceptionService中RunAsync方法的while循环首选什么:使用IsCancellationRequested或抛出异常
【发布时间】:2016-09-28 06:00:59
【问题描述】:

为什么新 Service Fabric 代码的示例 RunAsync 采用这样的结构

protected override async Task RunAsync(CancellationToken cancellationToken)
{
  while(true)
  {
    cancellationToken.ThrowIfCancellationRequested();

    await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
  }
} 

而不是这个

protected override async Task RunAsync(CancellationToken cancellationToken)
{
  while(cancellationToken.IsCancellationRequested)
  {
    await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
  }
} 

没有 throw 的版本不是首选吗? The docs 声明两种实现都是正确的:“系统将等待您的任务结束(通过成功完成、取消或故障)”。

【问题讨论】:

  • 我对Service Fabric还不是很熟悉,但是设置CancellationToken的预期语义是抛出OperationCanceledException如果它被取消,然后返回如果取消太晚了。他们可能只是为了保持一致性而使用此代码,或者 Service Fabric 可能会根据 RunAsync 的完成方式来更改其自我修复的方式。
  • 是的,这主要是为什么我们将示例代码更改为抛出 OperationCanceledException,其中 RunAsync 中的连续循环被 Service Fabric 通过发出取消令牌信号中断,因为 RunAsync 循环正在被取消,这是预期的语义。通过向系统报告故障,对其他异常类型进行不同的处理以进行自我修复。

标签: azure-service-fabric


【解决方案1】:

简短的回答 - 两者都很好。我会使用ThrowIfCancellationRequested,因为 IMO 它是更安全的选择。它也更加一致 - 调用链中较低的方法可以通过异常传播取消。

每当RunAsync 中发生异常时,Service Fabric reports 都会出现暂时性故障(这意味着在没有重新创建实例/副本的情况下重新启动服务)。

它对OperationCanceledException进行了特殊处理——如果它是由传递给方法的取消令牌抛出的,则认为该方法已成功取消,不会报告错误。

您可以通过监控Microsoft-ServiceFabric-Services ETW 事件自行尝试。

【讨论】:

    【解决方案2】:

    cancelToken.ThrowIfCancellationRequested();

    等于

    if (token.IsCancellationRequested) 抛出新的 OperationCanceledException(token);

    通常人们使用它作为一种控制机制来确保当前处理被中止,而不会运行任何额外的代码。此外,调用 ThrowIfCancellationRequested() 时无需检查是否取消

    【讨论】:

      【解决方案3】:

      这更简单,也很有效:

      protected override async Task RunAsync(CancellationToken cancellationToken)
      {
          while(true)
          {
              await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
          }
      }
      

      protected override async Task RunAsync(CancellationToken cancellationToken)
      {
          while(cancellationToken.IsCancellationRequested)
          {
              await Task.Delay(TimeSpan.FromSeconds(1));
          }
      }
      

      我发现了这一点,因为我的代码是按照这样的示例代码建模的:

      protected override async Task RunAsync(CancellationToken cancellationToken)     
      {
          var pipeline = ...
          pipeline.Start();
      
          while (true) {
              if (cancellationToken.IsCancellationRequested) {
                  ServiceEventSource.Current.ServiceMessage(Context, "Stopping pipeline");
                  await pipeline.ShutdownAsync();
              }
      
              cancellationToken.ThrowIfCancellationRequested();
      
              await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
          }
      }
      

      并且管道从未停止过。解决方案是在Task.Delay 调用中不使用cancellationToken

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-05-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多