IHostedService インターフェイスを実装する

指定された BackgroundService を超える有限制御が必要な場合は、独自の IHostedService を実装できます。 IHostedService インターフェイスは、.NET において実行時間の長いすべてのサービスの基礎となります。 カスタム実装は、AddHostedService<THostedService>(IServiceCollection) 拡張メソッドで登録されます。

このチュートリアルでは、次の作業を行う方法について説明します。

  • IHostedService および IAsyncDisposable インターフェイスを実装します。
  • タイマーベースのサービスを作成します。
  • 依存関係の挿入とログ記録にカスタム実装を登録します。

ヒント

".NET でのワーカー" のサンプル ソース コードはすべて、サンプル ブラウザーでダウンロードできます。 詳細については、コード サンプルの参照: .NET でのワーカーに関するページをご覧ください。

前提条件

新しいプロジェクトを作る

Visual Studio を使用して新しい Worker サービス プロジェクトを作成するには、[ファイル]>[新規]>[プロジェクト] を選択します。[新しいプロジェクトの作成] ダイアログで "Worker サービス" を検索し、Worker サービス テンプレートを選択します。 .NET CLI を使用する場合は、作業ディレクトリで好みのターミナルを開きます。 dotnet new コマンドを実行し、<Project.Name> を目的のプロジェクト名に置き換えます。

dotnet new worker --name <Project.Name>

.NET CLI の new worker サービス プロジェクト コマンドの詳細については、「dotnet new worker」を参照してください。

ヒント

Visual Studio Code を使用している場合は、統合ターミナルから .NET CLI コマンドを実行できます。 詳細については、Visual Studio Code の統合ターミナルに関する記事を参照してください。

タイマー サービスを作成する

タイマーベースのバックグラウンド サービスでは、System.Threading.Timer クラスを使用します。 タイマーによって DoWork メソッドがトリガーされます。 タイマーは IHostLifetime.StopAsync(CancellationToken) で無効になり、IAsyncDisposable.DisposeAsync() でサービス コンテナーが破棄されたときに破棄されます。

テンプレートの Worker の内容を次の C# コードに置き換え、ファイルの名前を TimerService.cs に変更します。

namespace App.TimerHostedService;

public sealed class TimerService(ILogger<TimerService> logger) : IHostedService, IAsyncDisposable
{
    private readonly Task _completedTask = Task.CompletedTask;
    private int _executionCount = 0;
    private Timer? _timer;

    public Task StartAsync(CancellationToken stoppingToken)
    {
        logger.LogInformation("{Service} is running.", nameof(TimerHostedService));
        _timer = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.FromSeconds(5));

        return _completedTask;
    }

    private void DoWork(object? state)
    {
        int count = Interlocked.Increment(ref _executionCount);

        logger.LogInformation(
            "{Service} is working, execution count: {Count:#,0}",
            nameof(TimerHostedService),
            count);
    }

    public Task StopAsync(CancellationToken stoppingToken)
    {
        logger.LogInformation(
            "{Service} is stopping.", nameof(TimerHostedService));

        _timer?.Change(Timeout.Infinite, 0);

        return _completedTask;
    }

    public async ValueTask DisposeAsync()
    {
        if (_timer is IAsyncDisposable timer)
        {
            await timer.DisposeAsync();
        }

        _timer = null;
    }
}

重要

WorkerBackgroundService のサブクラスでした。 現在は、TimerService によって IHostedServiceIAsyncDisposable の両方のインターフェイスが実装されています。

TimerServicesealed であり、DisposeAsync の呼び出しをその _timer インスタンスからカスケードします。 "破棄パターンのカスケード" の詳細については、「DisposeAsync メソッドの実装」を参照してください。

StartAsync が呼び出されると、タイマーがインスタンス化されるため、タイマーが開始されます。

ヒント

前の DoWork の実行が完了するまで Timer は待機されないため、ここで示したアプローチはすべてのシナリオに適しているとは限りません。 Interlocked.Increment は、アトミック操作として実行カウンターをインクリメントするために使用されされます。これにより、複数のスレッドによって _executionCount が同時に更新されなくなります。

既存の Program の内容を、次の C# コードに置き換えます。

using App.TimerHostedService;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddHostedService<TimerService>();

IHost host = builder.Build();
host.Run();

サービスは、AddHostedService 拡張メソッドを使用して (Program.cs) に登録されます。 これは、BackgroundService サブクラスの登録時に使用するのと同じ拡張メソッドであり、両方とも IHostedService インターフェイスを実装しています。

サービスの登録の詳細については、「.NET での依存関係の挿入」を参照してください。

サービスの機能を確認する

Visual Studio からアプリケーションを実行するには、F5 キーを押すか、[デバッグ]>[デバッグの開始] メニュー オプションを選択します。 .NET CLI を使用している場合は、作業ディレクトリから dotnet run コマンドを実行します。

dotnet run

.NET CLI の run コマンドの詳細については、「dotnet run」を参照してください。

アプリケーションをしばらく実行させて、何回か実行カウントのインクリメントを生成します。 次のような出力が表示されます。

info: App.TimerHostedService.TimerService[0]
      TimerHostedService is running.
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
      Content root path: .\timer-service
info: App.TimerHostedService.TimerService[0]
      TimerHostedService is working, execution count: 1
info: App.TimerHostedService.TimerService[0]
      TimerHostedService is working, execution count: 2
info: App.TimerHostedService.TimerService[0]
      TimerHostedService is working, execution count: 3
info: App.TimerHostedService.TimerService[0]
      TimerHostedService is working, execution count: 4
info: Microsoft.Hosting.Lifetime[0]
      Application is shutting down...
info: App.TimerHostedService.TimerService[0]
      TimerHostedService is stopping.

Visual Studio 内からアプリケーションを実行している場合は、[デバッグ]>[デバッグの停止] を選択します。または、コンソール ウィンドウで Ctrl + C キーを押して、キャンセルを通知します。

関連項目

関連するチュートリアルがいくつかあります。