Background Tasks in .NET 8

Dec 04 2023

Many thanks to the sponsors who make it possible for this newsletter to be free for readers.

 

• If you have ever used Postman to debug and write tests for your REST APIs, guess what, those are the same concepts you need to know for writing tests for your gRPC requests in Postman
For more info about gRPC, they created a great beginner article here .

 
 

The Background

 
 

What is Background task? What is Hosted Service?

 

A Background Task in .NET typically refers to any operation that runs in the background , separate from the main flow of your application. This can include tasks like processing data, performing I/O operations, or any other long-running processes that you don't want to run on the main thread because they might block the user interface or the main operation of your application.

 

A Hosted Service in .NET, specifically in the context of ASP.NET Core, is a service that is hosted within the application's process . It's a way to run long-running background tasks that are tied to the lifecycle of the application.

 

In today's article, I'll explore a recently introduced aspect of the Microsoft.Extensions.Hosting library, which has been part of .NET 8 from its fourth preview, specifically focusing on how it impacts hosted services.

 

Let's dive in...

 
 

What was wrong before .NET 8?

 
 

In earlier versions leading up to .NET 8, the initiation and termination of hosted services within the framework were managed in a sequential manner.

 

Specifically, each service registered as an IHostedService in the Dependency Injection (DI) container was started one after the other.

 

This process involved invoking the StartAsync method for each service and waiting for its completion before moving on to the next one . While this approach generally did not pose significant issues for most applications, there were scenarios where it could lead to complications.

 

For example, if a hosted service executed a lengthy process in its StartAsync method, it could inadvertently delay the startup of subsequent services in the application .

 

It is advisable to minimize the workload in the StartAsync method, but the potential for a slow service to hinder the overall application startup remained a concern prior to the introduction of .NET 8.

 

Let's see the example of how the background service was implemented before .NET 8:

public class SomeService : IHostedService
{
    public async Task StartAsync(CancellationToken cancellationToken)
    {
        Console.WriteLine("Start Service");
        await Task.Delay(60000); //1 minute
    }

    public async Task StopAsync(CancellationToken cancellationToken)
    {
        Console.WriteLine("Stop Service");
    }
}
DI registration:

builder.Services.AddHostedService<SomeService>();
So what is the problem here?

 

As I mentioned above, StartAsync will be started and will have to wait for it to finish before moving on to the next service. At this point the application won't actually start yet because DI hasn't registered all the services.

Before .NET 8 Testing

I simulated a job that takes 6 seconds and as you can see, 6 seconds passed before the application started.

 

This can be a problem. But there is a solution.

 
 

.NET 8 Fix

 
 

In .NET 8, you now have the option to start or stop your services at the same time, instead of one after the other. This is done by changing the settings in HostOptions . If you want all your services to start together, turn on the ServicesStartConcurrently option. And if you want them to stop at the same time, there's a setting for that too, called ServicesStopConcurrently

 

Let' take a look on the example:

builder.Services.Configure<HostOptions>(options =>
{
    options.ServicesStartConcurrently = true;
    options.ServicesStopConcurrently = true;
});

builder.Services.AddHostedService<SomeService>();
The result of this - other services started without any delay.

Fix with .NET 8 version Testing

More control over hosted services

 

.NET 8 introduced a new interface for hosted services - IHostedLifeCycleService . The reason for this is that we don't have much control over the services within the StartAsync() and StopAsync() methods.

 

This interface abstracts 4 more new methods:

 

• StartingAsync
• StartedAsync
• StoppingAsync
• StoppedAsync

public class SomeService : IHostedLifecycleService
{
    public async Task StartAsync(CancellationToken cancellationToken)
    {
        Console.WriteLine("Start Service");
    }

    public async Task StartedAsync(CancellationToken cancellationToken)
    {
        Console.WriteLine("Started Service");
    }

    public async Task StartingAsync(CancellationToken cancellationToken)
    {
        Console.WriteLine("Starting Service");
    }

    public async Task StopAsync(CancellationToken cancellationToken)
    {
        Console.WriteLine("Stop Service");
    }

    public async Task StoppedAsync(CancellationToken cancellationToken)
    {
        Console.WriteLine("Stopped Service");
    }

    public async Task StoppingAsync(CancellationToken cancellationToken)
    {
        Console.WriteLine("Stopping Service");
    }
}
This allows us to have full control over hosted services, while they starting (Starting), during their startup (Start), and when they are started (Started) - same for Stop, also.

 
 

Wrapping up

 
 

Before .NET 8, when an app started, its hosted services would start one after the other. Each service had to finish starting before the next one could begin. 
This usually didn't cause problems, but sometimes a slow service could delay the whole app from starting.
In .NET 8, two new features let us start or stop services simultaneously, instead of one after another. You can turn this on by changing the settings in HostOptions for any services you're using.

 

In addition, now you can have a full control over Hosted Services by implementing a new interface IHostedLifecycleService which comes with 4 new methods for controlling the service (StartedAsync, StartingAsync, StoppedAsync, StoppingAsync).

 

Install .NET 8 SDK and test, I'm sure you will see the benefits of this.

 

That's all from me today.

Join 10,950+ subscribers to improve your .NET Knowledge.

There are 3 ways I can help you:

Design Patterns Simplified ebook

Go-to resource for understanding the core concepts of design patterns without the overwhelming complexity. In this concise and affordable ebook, I've distilled the essence of design patterns into an easy-to-digest format. It is a Beginner level. Check out it here.


Sponsorship

Promote yourself to 10,950+ subscribers by sponsoring this newsletter.


Join .NET Pro Weekly Newsletter

Every Monday morning, I share 1 actionable tip on C#, .NET & Arcitecture topic, that you can use right away.


Subscribe to
.NET Pro Weekly

Subscribe to the .NET Pro Weekly and be among the 10,950+ subscribers gaining practical tips and resources to enhance your .NET expertise.