In this lesson, you will learn how to simplify the creation of long-running background tasks by leveraging the BackgroundService base class instead of implementing IHostedService manually.
Moving Beyond IHostedService
While IHostedService provides the raw foundation for background work, it requires you to manage the entire lifecycle manually—including thread safety and ensuring your service stops gracefully when the application shuts down. The BackgroundService class acts as a template, abstracting away the complex plumbing so you can focus on your actual business logic.
Implementing the Base Class
To use it, you simply inherit from BackgroundService and override the ExecuteAsync(CancellationToken stoppingToken) method. The runtime manages the startup and shutdown sequence for you, automatically passing in a stoppingToken that signals when the host is ready to terminate.
Here is how you implement a simple worker that performs a recurring task:
public class PeriodicTaskWorker : BackgroundService
{
private readonly ILogger<PeriodicTaskWorker> _logger;
public PeriodicTaskWorker(ILogger<PeriodicTaskWorker> logger)
{
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
// Execute loop while the application is running
while (!stoppingToken.IsCancellationRequested)
{
_logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
// Perform background work
await DoWorkAsync();
// Wait for a period without blocking thread pool threads
await Task.Delay(TimeSpan.FromSeconds(30), stoppingToken);
}
}
}
Why this is robust
- Graceful Shutdown: By observing the
stoppingTokenin yourTask.Delayand your logic, the service reacts immediately to application shutdown requests, preventing incomplete operations. - Simplified Lifecycle: You no longer need to worry about
StartAsyncorStopAsyncboilerplate; the base class handles the Task return types and task management automatically. - Readability: Your background logic remains isolated within a single, predictable loop, making it significantly easier to maintain compared to manually managing task states in a custom
IHostedServiceimplementation.
Key Takeaway: Inheriting from BackgroundService allows you to focus purely on your execution logic while gaining built-in, robust handling of the application lifecycle and cancellation signals.