IHostedServiceとBackgroundService
IHostedServiceと抽象クラスBackgroundServiceを実装し、.NETホスト内でバックグラウンド処理を実行します。
「IHostedServiceとBackgroundService」はCoddyKit上の無料C# Academyレッスンです。 これはレッスン1/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはC# Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 C# Academyコースには全4レッスンが含まれています。
バックグラウンドサービスとは
バックグラウンドサービスは、ASP.NET Coreアプリと並行してキューの処理、スケジュールされたタスクの実行、リソースの監視などの長時間実行処理を行います。.NETには、IHostedService(最小限の機能)と抽象クラスBackgroundService(ループ処理向け)の2つのインターフェイスが用意されています。
IHostedServiceインターフェイス
IHostedServiceには、StartAsync(ホストの開始時に呼び出されます)とStopAsync(正常なシャットダウン時に呼び出されます)の2つのメソッドがあります。起動時やシャットダウン時に一度だけ実行する処理に適しています。
public class DatabaseMigratorService : IHostedService
{
private readonly IServiceScopeFactory _scopeFactory;
public DatabaseMigratorService(IServiceScopeFactory sf) => _scopeFactory = sf;
public async Task StartAsync(CancellationToken ct)
{
using var scope = _scopeFactory.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
await db.Database.MigrateAsync(ct);
}
public Task StopAsync(CancellationToken ct) => Task.CompletedTask;
}
builder.Services.AddHostedService<DatabaseMigratorService>();BackgroundService:長時間実行ループ
BackgroundServiceはIHostedServiceを実装する抽象クラスで、アプリの存続期間中実行されるExecuteAsync(CancellationToken)メソッドを提供します。
public class HeartbeatService : BackgroundService
{
private readonly ILogger<HeartbeatService> _logger;
public HeartbeatService(ILogger<HeartbeatService> l) => _logger = l;
protected override async Task ExecuteAsync(CancellationToken ct)
{
_logger.LogInformation("Heartbeat service started");
while (!ct.IsCancellationRequested)
{
_logger.LogInformation("Heartbeat: {Time}", DateTime.UtcNow);
await Task.Delay(TimeSpan.FromSeconds(30), ct);
}
_logger.LogInformation("Heartbeat service stopping");
}
}
builder.Services.AddHostedService<HeartbeatService>();Scopedサービスの利用
バックグラウンドサービスはSingletonです。DbContextのようなScopedサービスを使用する場合は、必ずIServiceScopeFactoryを使って、処理単位ごとに新しいスコープを作成してください。
public class DataSyncService : BackgroundService
{
private readonly IServiceScopeFactory _factory;
private readonly ILogger<DataSyncService> _log;
public DataSyncService(IServiceScopeFactory f, ILogger<DataSyncService> l)
{ _factory = f; _log = l; }
protected override async Task ExecuteAsync(CancellationToken ct)
{
while (!ct.IsCancellationRequested)
{
using (var scope = _factory.CreateScope())
{
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
await SyncDataAsync(db, ct);
} // scope and DbContext disposed here
await Task.Delay(TimeSpan.FromMinutes(5), ct);
}
}
}正常なシャットダウン
ホストがシャットダウンすると、キャンセレーショントークンが通知されます。ExecuteAsyncでこのトークンを監視し、後片付けを行ってください。ホストは強制停止する前に、デフォルトで最大5秒間待機します。
protected override async Task ExecuteAsync(CancellationToken ct)
{
try
{
while (!ct.IsCancellationRequested)
{
await DoWorkAsync(ct);
await Task.Delay(1000, ct);
}
}
catch (OperationCanceledException)
{
// Normal shutdown — not an error
_logger.LogInformation("Service stopping due to cancellation");
}
finally
{
// Cleanup
await CleanupAsync();
}
}Channelベースのバックグラウンドキュー
よく使われるパターンとして、バックグラウンドサービスがChannelキューから読み取り、HTTPエンドポイントが処理項目をキューに追加します。これにより、リクエスト処理と実際の処理を分離できます。
public class BackgroundTaskQueue
{
private readonly Channel<Func<CancellationToken, Task>> _queue
= Channel.CreateBounded<Func<CancellationToken, Task>>(100);
public ValueTask QueueAsync(Func<CancellationToken, Task> job)
=> _queue.Writer.WriteAsync(job);
public IAsyncEnumerable<Func<CancellationToken, Task>> DequeueAllAsync(
CancellationToken ct)
=> _queue.Reader.ReadAllAsync(ct);
}
// Register and use:
builder.Services.AddSingleton<BackgroundTaskQueue>();
builder.Services.AddHostedService<QueueProcessorService>();バックグラウンドサービスのエラー処理
ExecuteAsyncで処理されない例外が発生すると、サービスが何も通知せずに停止する可能性があります。メインループをtry/catchで囲み、エラーをログに記録して、個々の失敗が発生してもサービスが稼働し続けるようにしてください。
protected override async Task ExecuteAsync(CancellationToken ct)
{
while (!ct.IsCancellationRequested)
{
try
{
await ProcessNextBatchAsync(ct);
}
catch (OperationCanceledException) when (ct.IsCancellationRequested)
{
break; // normal shutdown
}
catch (Exception ex)
{
_logger.LogError(ex, "Background processing error. Retrying in 5s.");
await Task.Delay(5000, ct); // backoff before retry
}
}
}StopAsyncとシャットダウンのタイムアウト
後片付けを行うにはStopAsyncをオーバーライドしてください。ホストからシャットダウン用のトークンが渡されます。後片付けがタイムアウト時間を超えると、プロセスは強制的に終了されます。
public override async Task StopAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("Service is stopping...");
// Signal internal work to stop
_internalCts.Cancel();
// Wait for the Execute loop to complete (up to timeout)
await base.StopAsync(stoppingToken);
_logger.LogInformation("Service stopped");
}
// Increase the shutdown timeout if needed:
builder.Services.Configure<HostOptions>(opt =>
opt.ShutdownTimeout = TimeSpan.FromSeconds(30));複数のバックグラウンドサービス
複数のホストサービスを登録すると、それらはすべて並行して実行されます。ホストは登録順に開始し、逆順に停止します。
builder.Services.AddHostedService<DatabaseMigratorService>(); // startup task
builder.Services.AddHostedService<MetricsCollectorService>(); // continuous
builder.Services.AddHostedService<EmailNotificationService>(); // continuous
builder.Services.AddHostedService<CacheWarmupService>(); // startup task
// All run concurrently after app start
// Stopped in reverse registration order on shutdown実践例:注文処理ワーカー
データベースのキューから保留中の注文を取得し、適切なスコープ管理とエラー処理を行いながら処理するバックグラウンドサービスです。
public class OrderProcessorService : BackgroundService
{
private readonly IServiceScopeFactory _factory;
private readonly ILogger<OrderProcessorService> _log;
public OrderProcessorService(IServiceScopeFactory f, ILogger<OrderProcessorService> l)
{ _factory = f; _log = l; }
protected override async Task ExecuteAsync(CancellationToken ct)
{
while (!ct.IsCancellationRequested)
{
try
{
using var scope = _factory.CreateScope();
var processor = scope.ServiceProvider.GetRequiredService<IOrderProcessor>();
int count = await processor.ProcessPendingAsync(ct);
_log.LogInformation("Processed {Count} orders", count);
}
catch (Exception ex) when (!ct.IsCancellationRequested)
{
_log.LogError(ex, "Order processing failed");
}
await Task.Delay(TimeSpan.FromSeconds(10), ct);
}
}
}理解度チェック
バックグラウンドサービスでは、DbContextを直接注入せずにIServiceScopeFactoryを使用する必要があるのはなぜですか?
まとめ:IHostedServiceとBackgroundService
重要なポイント:
IHostedService:StartAsync/StopAsync — 起動時やシャットダウン時の処理に使用しますBackgroundService:ExecuteAsyncループ — 継続的なバックグラウンド処理に使用します- バックグラウンドサービス内でScoped依存関係を使用する場合は、必ず
IServiceScopeFactoryを使用します - 正常なシャットダウンのため、すべてのawaitでキャンセレーショントークンを監視します
- エラーでサービスが停止しないよう、ループ本体をtry/catchで囲みます
- 後片付けに5秒以上必要な場合は、
HostOptions.ShutdownTimeoutを使用します
AI チューターと学ぶ C# — 無料
ブラウザでリアルコードを書いて実行し、24/7 の AI チューターから瞬時にサポートを受け、ウェブまたはアプリで続きから学習できます。
- コース
- 93
- レッスン
- 346
よくある質問
「IHostedServiceとBackgroundService」レッスンは無料ですか?
はい。「IHostedServiceとBackgroundService」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、C# Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 C# Academyコースには全4レッスンが含まれています。
「IHostedServiceとBackgroundService」で何を学びますか?
IHostedServiceと抽象クラスBackgroundServiceを実装し、.NETホスト内でバックグラウンド処理を実行します。 ブラウザで直接実行するハンズオンコードでC# Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
C# Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのC# Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン1/4です。
「IHostedServiceとBackgroundService」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このC# Academyレッスンでコードを書いて実行できますか?
はい。すべてのC# Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- IHostedServiceとBackgroundService
- Worker Serviceプロジェクト
- 定期タスクとタイマー
- Quartz.NETのスケジュールジョブ