title: Why does the worker service template not gracefully shut down as systemd?
body: <p>I created a new project using the worker service template and added two lines.</p>
  <p>One extra log output in the worker after being stopped:</p>
  <pre><code>    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
      {
          while (!stoppingToken.IsCancellationRequested)
          {
              _logger.LogInformation(&quot;Worker running at: {time}&quot;, DateTimeOffset.Now);
              await Task.Delay(1000, stoppingToken);
          }
          _logger.LogInformation(&quot;Worker stopped.&quot;); // &lt;---- extra log output
      }
  </code></pre>
  <p>And one in the Program.cs for systemd support:</p>
  <pre><code>    public static IHostBuilder CreateHostBuilder(string[] args) =&gt;
          Host.CreateDefaultBuilder(args)
              .UseSystemd() // &lt;---- systemd support
              .ConfigureServices((hostContext, services) =&gt;
              {
                  services.AddHostedService&lt;Worker&gt;();
              });
  </code></pre>
  <p>I can start and stop the service on my ubuntu server, but in the journalctl output the extra log line does not appear:</p>
  <pre><code>worker[25468]: WorkerService.Worker[0] Worker running at: 02/10/2021 23:07:34 +01:00
  systemd[1]: Stopping Long running service/daemon created from .NET worker template...
  worker[25468]: Microsoft.Hosting.Lifetime[0] Application is shutting down...
  // here I would expect &quot;Worker stopped.&quot;
  systemd[1]: Stopped Long running service/daemon created from .NET worker template.
  </code></pre>
  <p>How can I make sure that my background service runs to completion before the service stops?</p>
  <p>I have found this very similar question, unfortunately it has no satisfying answer:
  <a href="https://stackoverflow.com/questions/61936664/net-core-backgroundservice-does-not-shut-down-gracefully-as-daemon">.NET core BackgroundService does not shut down gracefully as daemon</a></p>
  <p>But I can't believe there isn't a simple answer to this.</p>
  <p><strong>UPDATE:</strong></p>
  <p>I found a way to gracefully shut down the service.
  Instead of waiting for the <code>stoppingToken</code> in the <code>ExecuteAsync()</code> to be triggered, I am checking a flag, whether the service should stop running, which is set in the <code>StopAsync()</code> method. Using a <code>ManualResetEvent</code> I can then decide, if the service should wait for the Backgroundservice to finish its work and shut down gracefully, or if it should force a shut down after a given timeout.
  This is probably not the most elegant way to do it, but it seems to do what I want.</p>
  <pre><code>    private bool _stopRunning;
      private ManualResetEvent _mre;
      private int _graceperiod = 5000;
      protected override async Task ExecuteAsync(CancellationToken stoppingToken)
      {
          _stopRunning = false;
          _mre = new ManualResetEvent(false);
          int item = 1;
          while (!_stopRunning)
          {
              _logger.LogInformation(&quot;Working on item &quot; + item);
              await Task.Delay(3000);
              _logger.LogInformation(&quot;still working on item &quot; + item);
              await Task.Delay(3000);
              _logger.LogInformation(&quot;Finished working on item &quot; + item);
              item++;
          }
  
          _logger.LogInformation(&quot;Worker stopped.&quot;);
          _mre.Set();
      }
  
      public override async Task StopAsync(CancellationToken cancellationToken)
      {
          _stopRunning = true;
          bool gracefulShutdown = _mre.WaitOne(_graceperiod);
          if (gracefulShutdown)
          {
              _logger.LogInformation(&quot;Shut down service gracefully&quot;);
          }
          else
          {
              _logger.LogInformation(&quot;Force shut down service&quot;);
          }
          await base.StopAsync(cancellationToken);
      }
  </code></pre>
  <p>I still would like to understand though, how the stoppingToken is supposed to be triggered.</p>