Killing a Worker Application from a BackgroundService
Download full source code.
If you have an application that runs a BackgroundService
or maybe a few BackgroundService
s, you may want to stop the host application and all the other services from one of the BackgroundService
s .
The first approach I tried was to pass a CancellationTokenSource
to the service using dependency injection and then call Cancel()
on it. The host application monitors the CancellationToken
, and when it is cancelled, it stops the host application.
But then I found another way that is probably a little more “correct”. I pass the IHostApplicationLifetime
to the service and call StopApplication()
on it. This will stop the host application and all the other services that might be running.
This blog post will show you how to do this. In another post, I show the other approach mentioned above.
1. Create the project
Create a worker project -
dotnet new worker -n KillHostFromWorker
2. Update the Program.cs
Open the project and update the Program.cs
file to look like this -
1using KillHostFromWorker;
2
3var builder = Host.CreateApplicationBuilder(args);
4builder.Services.AddHostedService<Worker>(); // simple worker
5builder.Services.AddHostedService<WorkerCancelHost>(); // worker that cancels the host
6
7var host = builder.Build();
8host.Run();
9
10Console.WriteLine("Host exiting");
This adds two workers to the host. The first one is a simple worker that prints out a count. The second one is the worker that will cancel the host after running for a few seconds.
2. Update the Worker.cs
This is not strictly necessary, but it will make the output a little clearer, especially when this worker gets cancelled during its Task.Delay(...)
call. Open the Worker.cs
file and update it to look like this -
1namespace KillHostFromWorker;
2
3public class Worker : BackgroundService
4{
5 protected override async Task ExecuteAsync(CancellationToken stoppingToken)
6 {
7 int count = 0;
8 while (!stoppingToken.IsCancellationRequested)
9 {
10 Console.WriteLine($"Worker: count = {++count} ");
11 try
12 {
13 await Task.Delay(1000, stoppingToken);
14 }
15 catch (TaskCanceledException)
16 {
17 Console.WriteLine("Worker: Task was cancelled during Task.Delay");
18 }
19 }
20 Console.WriteLine("Worker: Exiting");
21 }
22}
3. Add a WorkerCancelHost.cs
file
This is the worker that will cancel the host, and in turn, all other workers.
Create a file class named WorkerCancelHost
and add the following code -
1namespace KillHostFromWorker;
2
3public class WorkerCancelHost(IHostApplicationLifetime hostApplicationLifetime) : BackgroundService
4{
5 protected override async Task ExecuteAsync(CancellationToken stoppingToken)
6 {
7 int count = 0;
8 while (!stoppingToken.IsCancellationRequested)
9 {
10 Console.WriteLine($"\tWorkerCancelHost: count = {++count}");
11
12 if(count >= 3)
13 {
14 Console.WriteLine("\tWorkerCancelHost: Cancelling host from WorkerCancelHost");
15 hostApplicationLifetime.StopApplication();
16 break;
17 }
18
19 try
20 {
21 await Task.Delay(1000, stoppingToken);
22 }
23 catch (TaskCanceledException)
24 {
25 Console.WriteLine("\tWorkerCancelHost: Task was cancelled during Task.Delay");
26 break;
27 }
28 }
29 Console.WriteLine("\tWorkerCancelHost: Exiting");
30 }
31}
Note, on line 3, the IHostApplicationLifetime
is passed to the constructor.
It is then used to call StopApplication()
after the worker has run for three seconds.
4. Run the application
Run the application and you should see output like this -
Worker: count = 1
WorkerCancelHost: count = 1
WorkerCancelHost: count = 2
Worker: count = 2
Worker: count = 3
WorkerCancelHost: count = 3
WorkerCancelHost: Cancelling host from WorkerCancelHost
WorkerCancelHost: Exiting
Worker: Task was cancelled during Task.Delay
Worker: Exiting
Host exiting
When working with applications where multiple events can terminate it, it is always a good idea to test the cancellation paths.
Now try running it, but press Ctrl+C
to cancel the host. You should see output like this -
Worker: count = 1
WorkerCancelHost: count = 1
WorkerCancelHost: count = 2
Worker: count = 2
^C WorkerCancelHost: Task was cancelled during Task.Delay
WorkerCancelHost: Exiting
Worker: Task was cancelled during Task.Delay
Worker: Exiting
Host exiting
Download full source code.