停止子系统:单个 CancellationToken 与 Stop 方法

Stopping subsystems: single CancellationToken vs Stop method

我有一个简单的 .net 核心控制台应用程序,其中包含几个长 运行 后台服务。服务在应用程序启动时启动。我想根据用户请求正确停止它们。 Microsoft 提供了基础 class 用于实现长 运行 - BackgroundService.

 public abstract class BackgroundService : IHostedService, IDisposable
  {
    private readonly CancellationTokenSource _stoppingCts = new CancellationTokenSource();
    private Task _executingTask;

    /// <summary>
    /// This method is called when the <see cref="T:Microsoft.Extensions.Hosting.IHostedService" /> starts. The implementation should return a task that represents
    /// the lifetime of the long running operation(s) being performed.
    /// </summary>
    /// <param name="stoppingToken">Triggered when <see cref="M:Microsoft.Extensions.Hosting.IHostedService.StopAsync(System.Threading.CancellationToken)" /> is called.</param>
    /// <returns>A <see cref="T:System.Threading.Tasks.Task" /> that represents the long running operations.</returns>
    protected abstract Task ExecuteAsync(CancellationToken stoppingToken);

    /// <summary>
    /// Triggered when the application host is ready to start the service.
    /// </summary>
    /// <param name="cancellationToken">Indicates that the start process has been aborted.</param>
    public virtual Task StartAsync(CancellationToken cancellationToken)
    {
      this._executingTask = this.ExecuteAsync(this._stoppingCts.Token);
      if (this._executingTask.IsCompleted)
        return this._executingTask;
      return Task.CompletedTask;
    }

    /// <summary>
    /// Triggered when the application host is performing a graceful shutdown.
    /// </summary>
    /// <param name="cancellationToken">Indicates that the shutdown process should no longer be graceful.</param>
    public virtual async Task StopAsync(CancellationToken cancellationToken)
    {
      if (this._executingTask == null)
        return;
      try
      {
        this._stoppingCts.Cancel();
      }
      finally
      {
        Task task = await Task.WhenAny(this._executingTask, Task.Delay(-1, cancellationToken));
      }
    }

    public virtual void Dispose()
    {
      this._stoppingCts.Cancel();
    }

BackgroundService 允许通过调用方法 StopAsync 停止服务。 我们也可以通过这种方式实现长 运行 服务,使用单一方法和取消标记:

public class LongRunningService
{
    public Task RunAsync(CancellationToken token)
    {
        return Task.Factory.StartNew(() =>
            {
                // here goes something long running
                while (!token.IsCancellationRequested)
                {

                }
            },
            TaskCreationOptions.LongRunning);
    }
}

两种方法都解决了我的问题。但是在代码组织和匹配class语义方法方面我无法决定哪一个更好。您会选择哪种方法,为什么?

经过几年的发展,我准备好回答我自己的问题了。希望这会对其他人有所帮助。

如果您需要在应用程序的整个生命周期中处理某些东西,您应该选择后台服务(例如看门狗、评级更新程序、后台 email/SMS 发件人)。

如果你只是需要运行任何一种long-运行ning,但是限时操作,你应该选择Task.Factory.StartNew和long-运行宁选项。

在大多数情况下,当您创建 .NET Core 应用程序时,后台服务适合您的大多数情况,因为在网络应用程序或后台工作人员中通常不需要 运行ning long-运行从您的代码中手动执行任务。