title: Graceful shutdown of IHostedService / BackgroundService
body: <p>What is the best way to block shutdown of a service while jobs finish? I have a few background services that listen for messages on a message bus and handle processing.Some implement <code>IHostService</code> directly and some utilize <code>BackgroundService</code></p>
  <p>I know loops with a delay is absolutely not the best way, but this is the simplest illustration I can demonstrate. Another thought I had was a counter with number of total number of active jobs, but that would need a lock around it and I don't think that's a great idea either.</p>
  <pre><code>public interface IMessageBus
  {
     Task&lt;int&gt; Subscribe(CancellationToken cancellationToken);
     Task Unsubscribe(int subscriptionId, CancellationToken cancellationToken);
  }
  
  public class Worker : IHostedService, IDisposable
  {
      private readonly ILogger logger;
      private readonly IMessageBus messageBus;
      private readonly List&lt;Task&gt; activeTasks;
      private CancellationTokenSource stoppingCts;
      int? subscriptionId;
  
      public Worker(ILogger logger, IMessageBus messageBus)
      {
          this.logger = logger;
          this.messageBus = messageBus;
          this.activeTasks = new List&lt;Task&gt;();
      }
  
      public async Task StartAsync(CancellationToken cancellationToken)
      {
          this.stoppingCts = new CancellationTokenSource();
  
          this.logger.LogDebug(&quot;Subscribing to message bus.&quot;);
          this.subscriptionId = await this.messageBus.Subscribe(cancellationToken, this.Process);
      }
  
      public async Task StopAsync(CancellationToken cancellationToken)
      {
          this.logger.LogDebug(&quot;Stopping Worker&quot;);
  
          // this will stop new messages coming in....
          if (this.subscriptionId.HasValue)
          {
              this.logger.LogDebug(&quot;Unsubscribed from message bus&quot;);
              await this.messageBus.Unsubscribe(this.subscriptionId.Value, cancellationToken);
          }
  
          // this should block until existing jobs finished....i don't like it though
          this.logger.LogDebug(&quot;Waiting for all jobs to finish&quot;);
          while (!cancellationToken.IsCancellationRequested &amp;&amp; this.activeTasks.Count &gt; 0)
          {
              await Task.Delay(10);
              this.logger.LogDebug(&quot;Still waiting for all jobs to finish&quot;);
          }
  
          this.logger.LogDebug(&quot;Worker stopped.&quot;);
      }
  
      // this does real work, would be multiple running concurrently
      public async Task&lt;bool&gt; Process()
      {
          this.logger.LogInformation(&quot;Starting task..&quot;);
  
          var task = Task.Delay(100, this.stoppingCts.Token);
          this.activeTasks.Add(task);
          await task;
          this.activeTasks.Remove(task);
  
          this.logger.LogInformation(&quot;Task completed&quot;);
  
          return true;
      }
  
      public void Dispose()
      {
          this.stoppingCts?.Cancel();
      }
  }
  </code></pre>