DEV Community

Cover image for IHostedService vs. BackgroundService: qual usar e por quê
Paulo Walraven
Paulo Walraven

Posted on

IHostedService vs. BackgroundService: qual usar e por quê

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);
}
Enter fullscreen mode Exit fullscreen mode

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);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

O que está acontecendo aqui:

  • ExecuteAsync roda 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>();
Enter fullscreen mode Exit fullscreen mode

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.

Linha do tempo do ciclo de vida de um hosted service, do start ao shutdown

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)