Você herda de BackgroundService, sobrescreve ExecuteAsync, e tudo "simplesmente funciona". Mas você sabe o que essa classe esconde de você? Por baixo do açúcar sintático existe uma interface, um host genérico e um ciclo de vida que decide quando seu código começa e — mais importante — quando ele para.
Ao final deste post você vai saber exatamente o que é o IHostedService, o que o BackgroundService abstrai por cima dele, e quando vale a pena descer um nível e implementar a interface na mão.
O ponto de partida: o ciclo de vida da aplicação
Toda aplicação ASP.NET Core moderna se constrói sobre o generic host (.NET host). É ele quem gerencia injeção de dependência, configuração, logging e, principalmente, o startup e o shutdown da aplicação.
Os hosted services se conectam diretamente a esse ciclo de vida. A regra é simples:
- Quando a aplicação inicia, o hosted service inicia.
- Quando a aplicação para, o hosted service para.
Essa amarração ao ciclo de vida não é um detalhe — é justamente o que permite que uma tarefa em background pare graciosamente e libere seus recursos em vez de morrer no meio do trabalho.
IHostedService: a interface por baixo de tudo
A forma mais simples de rodar trabalho em background no .NET é um hosted service, e na base dele está a interface IHostedService. Ela define apenas dois métodos:
public interface IHostedService
{
Task StartAsync(CancellationToken cancellationToken);
Task StopAsync(CancellationToken cancellationToken);
}
Esses dois métodos definem o ciclo de vida do serviço:
-
StartAsyncé chamado quando a aplicação inicia. -
StopAsyncé chamado quando a aplicação para.
Repare no CancellationToken: ele não está ali por enfeite. É o que permite que a tarefa em background participe com segurança do shutdown, sinalizando "hora de parar" para que recursos como arquivos abertos ou conexões sejam liberados corretamente.
Tecnicamente, você pode implementar essa interface diretamente. Mas se fizer isso, todo o controle do loop, do estado e do encerramento fica nas suas mãos — e é aí que entra muito código repetitivo (boilerplate).
BackgroundService: o wrapper que você realmente usa
Para evitar esse boilerplate, o ASP.NET fornece uma classe base: BackgroundService. A maioria dos hosted services que você vê em produção usa exatamente essa classe.
A diferença é direta: em vez de implementar dois métodos de ciclo de vida, você sobrescreve um só:
public class JobProcessor : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
Console.WriteLine("Verificando jobs...");
await Task.Delay(1000, stoppingToken);
}
}
}
O que está acontecendo aqui:
-
ExecuteAsyncroda durante toda a vida da aplicação. - Dentro dele há um loop que continua até a aplicação desligar.
- O
stoppingTokené acionado no shutdown, o loop sai, e a tarefa termina de forma controlada.
E o ponto que confunde muita gente: você não implementa IHostedService aqui porque o BackgroundService já faz isso por você. Se você olhar dentro da classe (no namespace Microsoft.Extensions.Hosting), vai ver que ela implementa IHostedService e cuida do StartAsync/StopAsync internamente. O BackgroundService é, em essência, um wrapper elegante sobre a interface.
Registrando o serviço
Implementar a classe é metade do trabalho. Para o host realmente iniciar o serviço, você o registra no container de injeção de dependência, no program.cs:
builder.Services.AddHostedService<JobProcessor>();
Feito isso, o host inicia o serviço automaticamente quando a aplicação sobe. E você pode registrar quantos hosted services quiser — eles rodam lado a lado. Sobe a aplicação e ambos passam a executar seus loops em paralelo, cada um controlado pelo mesmo ciclo de vida que o IHostedService fornece.
Outro detalhe importante: isso funciona tanto em um Worker Service quanto em uma API ASP.NET Core. O mesmo AddHostedService<T>() e o mesmo BackgroundService servem nos dois cenários — a diferença é só onde o serviço vive.
Então, quando implementar IHostedService na mão?
Na prática, a resposta para a maioria dos casos é: use BackgroundService. Ele trata da estrutura de ciclo de vida, garante que a tarefa inicie com a aplicação, pare no shutdown e respeite o cancelamento — deixando você focar no trabalho que realmente importa.
Você só desce para IHostedService direto quando precisa de controle total sobre o start e o stop, por exemplo: lógica de inicialização que precisa rodar e terminar antes da aplicação ficar pronta, ou um encerramento que não combina com o modelo de "um loop único" do ExecuteAsync. Fora esses casos, o wrapper é a escolha certa — e mais segura.
Conclusão
IHostedService é o contrato; BackgroundService é a conveniência construída sobre ele. Entender essa relação faz você parar de tratar ExecuteAsync como mágica e passar a enxergar o ciclo de vida real por trás do seu código em background — o que é o primeiro passo para escrever serviços que param tão bem quanto começam.
Agora abra seu program.cs, crie um BackgroundService simples e observe-o iniciar e parar junto com a aplicação. No próximo post, vamos atacar um dos erros mais comuns com hosted services: injetar dependências scoped em um serviço que, na verdade, é singleton.

Top comments (0)