.NET Core 汎用ホスト で、メイン処理が終わったらアプリを自動的に終了させる仕組みのサンプルです。
ASP.NET Core はコンソール アプリケーションで Web サーバーをホストしますが、そこから Web サーバー機能を取り除いて、汎用的なコンソール アプリケーションで、以下のような機能を使えるようにしたものが 汎用ホスト です。
- Dependency Injection (DI)
- JSON や環境変数によるアプリケーション構成
- ロギング
- バックグラウンド サービス
なお、汎用ホストは .NET Core 2.1 からの新機能です。
汎用ホストには IHostedService インターフェイス を実装したクラスや BackgroundService クラス から派生したクラスを IServiceCollection.AddHostedService 拡張メソッド で登録してやることで、アプリケーションで簡単に複数のバックグラウンド サービスをホストすることができる仕組みが用意されています。
この仕組みはサーバー的な用途が想定されており、ユーザーが Ctrl+C などでホスト アプリケーションを終了させるまで、ずっと動き続けます。
ところで、コンソール アプリケーションの用途としてはもう一つ、起動したら一定の処理を行い、自動的に終了するという形態があります(というか、そっちの方がメインだと思います)。汎用ホストでは、このような形態を実現する機能は、直接的には用意されていません。
そこで作ったのがこれです。
Main メソッド内で IHost.RunAsync 拡張メソッド を呼ぶと、内部で WaitForShutdownAsync メソッド が呼ばれ、これによってホスト プロセスのメイン スレッドは終了待ち状態で停止します。
終了待ち状態を解除して Main の実行を再開し、プロセスを終了させるには、ワーカー スレッドから IApplicationLifetime インターフェイス の StopApplication メソッド を呼んでやる必要があります。
バックグラウンド サービスから呼んでもいいのですが、基本的にバックグラウンド サービスは、ずっと動き続ける性質のものであり、ホストの終了条件を判断することは少ないと思います。
そのため、ホスト プロセスの寿命を司る IHostLifetime インターフェイス というのが用意されており、これを実装したクラスが StopApplication メソッドを呼び出します。
汎用ホストには既定で ConsoleLifetime クラス が組み込まれており、Ctrl+C を押すと終了させるといった制御はこいつが行っています。
IForegroundService インターフェイス を実装したクラスを、便宜上 フォアグラウンド サービス と呼びます。
これは、普通のコンソール アプリケーションのように、一定の処理を行ったら自動的に終了することを想定しています。
ConsoleLifetime の代わりに ForegroundLifetime クラス を組み込むことで、フォアグラウンド サービスの実行が終了したら、自動的に StopApplication を呼び出して、ホストを終了させます。
使い方は サンプル を見てください。
これはフォアグラウンド サービスに限らず、バックグラウンド サービスにも言えることなのですが、サービス内で同期的にスレッドを止めてはいけません。 最悪、他のサービスが全部止まります。
バックグラウンド サービスは(半)無限ループを回して、その中で I/O 待ちなどの非同期処理を行うことが多いので、自然と非同期になると思いますが、フォアグラウンド サービスは、シングル スレッドなコンソール アプリケーションの使用感に似せたものなので、うっかり Console.ReadKey メソッド などで止めないようにしてください。
CancellationToken を 汎用的なシグナル機構として使う のはどうなんですかね…