ASP.NET Core でホステッド サービスを使用するバックグラウンド タスクBackground tasks with hosted services in ASP.NET Core
作成者: Jeow Li HuanBy Jeow Li Huan
ASP.NET Core では、バックグラウンド タスクを ホステッド サービス として実装することができます。In ASP.NET Core, background tasks can be implemented as hosted services. ホストされるサービスは、IHostedService インターフェイスを実装するバックグラウンド タスク ロジックを持つクラスです。A hosted service is a class with background task logic that implements the IHostedService interface. このトピックでは、3 つのホステッド サービスの例について説明します。This topic provides three hosted service examples:
- タイマーで実行されるバックグラウンド タスク。Background task that runs on a timer.
- スコープ サービスをアクティブ化するホステッド サービス。Hosted service that activates a scoped service. スコープ サービスは依存関係の挿入 (DI) を使用できます。The scoped service can use dependency injection (DI).
- 連続して実行される、キューに格納されたバックグラウンド タスク。Queued background tasks that run sequentially.
サンプル コードを表示またはダウンロードします (ダウンロード方法)。View or download sample code (how to download)
ワーカー サービス テンプレートWorker Service template
ASP.NET Core ワーカー サービス テンプレートは、実行時間が長いサービス アプリを作成する場合の出発点として利用できます。The ASP.NET Core Worker Service template provides a starting point for writing long running service apps. ワーカー サービス テンプレートから作成されたアプリで、そのプロジェクト ファイル内のワーカー SDK が指定されます。An app created from the Worker Service template specifies the Worker SDK in its project file:
<Project Sdk="Microsoft.NET.Sdk.Worker">
ホステッド サービス アプリの基礎としてテンプレートを使用するには:To use the template as a basis for a hosted services app:
- 新しいプロジェクトを作成します。Create a new project.
- [ワーカー サービス] を選択します。Select Worker Service. [次へ] を選択します。Select Next.
- [プロジェクト名] フィールドにプロジェクト名を入力するか、既定のプロジェクト名をそのまま使用します。Provide a project name in the Project name field or accept the default project name. 作成 を選択します。Select Create.
- [新しい Worker サービスを作成します] ダイアログで、 [作成] を選択します。In the Create a new Worker service dialog, select Create.
PackagePackage
ワーカー サービス テンプレートに基づくアプリは Microsoft.NET.Sdk.Worker
SDK を使用し、Microsoft.Extensions.Hosting パッケージへの明示的なパッケージ参照を含んでいます。An app based on the Worker Service template uses the Microsoft.NET.Sdk.Worker
SDK and has an explicit package reference to the Microsoft.Extensions.Hosting package. たとえば、サンプル アプリのプロジェクト ファイル (BackgroundTasksSample.csproj) を参照してください。For example, see the sample app's project file (BackgroundTasksSample.csproj).
Microsoft.NET.Sdk.Web
SDK を使用する Web アプリの場合、Microsoft.Extensions.Hosting パッケージは共有フレームワークから暗黙的に参照されます。For web apps that use the Microsoft.NET.Sdk.Web
SDK, the Microsoft.Extensions.Hosting package is referenced implicitly from the shared framework. アプリのプロジェクト ファイル内の明示的なパッケージ参照は必要ありません。An explicit package reference in the app's project file isn't required.
IHostedService インターフェイスIHostedService interface
IHostedService インターフェイスは、ホストによって管理されるオブジェクトの 2 つのメソッドを定義します。The IHostedService interface defines two methods for objects that are managed by the host:
StartAsync(CancellationToken):
StartAsync
には、バックグラウンド タスクを開始するロジックが含まれています。StartAsync(CancellationToken):StartAsync
contains the logic to start the background task.StartAsync
は、以下よりも "前に" 呼び出されます。StartAsync
is called before:- アプリの要求処理パイプラインが構成される (
Startup.Configure
)。The app's request processing pipeline is configured (Startup.Configure
). - サーバーが起動して、IApplicationLifetime.ApplicationStarted がトリガーされる。The server is started and IApplicationLifetime.ApplicationStarted is triggered.
既定の動作を変更して、アプリのパイプラインが構成されて
ApplicationStarted
が呼び出された後で、ホステッド サービスのStartAsync
が実行するようにできます。The default behavior can be changed so that the hosted service'sStartAsync
runs after the app's pipeline has been configured andApplicationStarted
is called. 既定の動作を変更するには、ConfigureWebHostDefaults
を呼び出した後でホステッド サービス (次の例ではVideosWatcher
) を追加します。To change the default behavior, add the hosted service (VideosWatcher
in the following example) after callingConfigureWebHostDefaults
:using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; public class Program { public static void Main(string[] args) { CreateHostBuilder(args).Build().Run(); } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); }) .ConfigureServices(services => { services.AddHostedService<VideosWatcher>(); }); }
- アプリの要求処理パイプラインが構成される (
StopAsync(CancellationToken):ホストが正常なシャットダウンを実行しているときにトリガーされます。StopAsync(CancellationToken): Triggered when the host is performing a graceful shutdown.
StopAsync
には、バックグラウンド タスクを終了するロジックが含まれています。StopAsync
contains the logic to end the background task. アンマネージ リソースを破棄するには、IDisposable とファイナライザー (デストラクター) を実装します。Implement IDisposable and finalizers (destructors) to dispose of any unmanaged resources.キャンセル トークンには、シャットダウン プロセスが正常に行われないことを示す、既定の 5 秒間のタイムアウトが含まれています。The cancellation token has a default five second timeout to indicate that the shutdown process should no longer be graceful. キャンセルがトークンに要求された場合:When cancellation is requested on the token:
- アプリで実行されている残りのバックグラウンド操作が中止します。Any remaining background operations that the app is performing should be aborted.
StopAsync
で呼び出されたすべてのメソッドが速やかに戻ります。Any methods called inStopAsync
should return promptly.
ただし、キャンセルが要求された後もタスクは破棄されません—呼び出し元がすべてのタスクの完了を待機します。However, tasks aren't abandoned after cancellation is requested—the caller awaits all tasks to complete.
アプリが予期せずシャットダウンした場合 (たとえば、アプリのプロセスが失敗した場合)、
StopAsync
は呼び出されないことがあります。If the app shuts down unexpectedly (for example, the app's process fails),StopAsync
might not be called. そのため、StopAsync
で呼び出されたメソッドや行われた操作が実行されない可能性があります。Therefore, any methods called or operations conducted inStopAsync
might not occur.既定の 5 秒のシャットダウン タイムアウトを延長するには、次を設定します。To extend the default five second shutdown timeout, set:
- ShutdownTimeout (汎用ホストを使用するとき)ShutdownTimeout when using Generic Host. 詳細については、「ASP.NET Core の .NET 汎用ホスト」を参照してください。For more information, see ASP.NET Core の .NET 汎用ホスト.
- シャットダウン タイムアウトのホスト構成設定 (Web ホストを使用するとき)Shutdown timeout host configuration setting when using Web Host. 詳細については、「ASP.NET Core の Web ホスト」を参照してください。For more information, see ASP.NET Core の Web ホスト.
ホステッド サービスは、アプリの起動時に一度アクティブ化され、アプリのシャットダウン時に正常にシャットダウンされます。The hosted service is activated once at app startup and gracefully shut down at app shutdown. バックグラウンド タスクの実行中にエラーがスローされた場合、StopAsync
が呼び出されていなくても Dispose
を呼び出す必要があります。If an error is thrown during background task execution, Dispose
should be called even if StopAsync
isn't called.
BackgroundService 基底クラスBackgroundService base class
BackgroundService は、長期 IHostedService を実装するための基底クラスです。BackgroundService is a base class for implementing a long running IHostedService.
ExecuteAsync(CancellationToken) は、バックグラウンド サービスを実行するために呼び出されます。ExecuteAsync(CancellationToken) is called to run the background service. この実装では、バックグラウンド サービスの有効期間全体を表す Task が返されます。The implementation returns a Task that represents the entire lifetime of the background service. await
を呼び出すなどして ExecuteAsync が非同期になるまで、以降のサービスは開始されません。No further services are started until ExecuteAsync becomes asynchronous, such as by calling await
. ExecuteAsync
では、長時間の初期化作業を実行しないようにしてください。Avoid performing long, blocking initialization work in ExecuteAsync
. ホストは StopAsync(CancellationToken) でブロックされ、ExecuteAsync
の完了を待機します。The host blocks in StopAsync(CancellationToken) waiting for ExecuteAsync
to complete.
IHostedService.StopAsync が呼び出されると、キャンセル トークンがトリガーされます。The cancellation token is triggered when IHostedService.StopAsync is called. サービスを正常にシャットダウンするためのキャンセル トークンが起動すると、ExecuteAsync
の実装はすぐに終了します。Your implementation of ExecuteAsync
should finish promptly when the cancellation token is fired in order to gracefully shut down the service. それ以外の場合は、シャットダウンのタイムアウト時にサービスが強制的にシャットダウンします。Otherwise, the service ungracefully shuts down at the shutdown timeout. 詳細については、「IHostedService インターフェイス」のセクションを参照してください。For more information, see the IHostedService interface section.
時間が指定されたバックグラウンド タスクTimed background tasks
時間が指定されたバックグラウンド タスクは、System.Threading.Timer クラスを利用します。A timed background task makes use of the System.Threading.Timer class. このタイマーはタスクの DoWork
メソッドをトリガーします。The timer triggers the task's DoWork
method. タイマーは StopAsync
で無効になり、Dispose
でサービス コンテナーが破棄されたときに破棄されます。The timer is disabled on StopAsync
and disposed when the service container is disposed on Dispose
:
public class TimedHostedService : IHostedService, IDisposable
{
private int executionCount = 0;
private readonly ILogger<TimedHostedService> _logger;
private Timer _timer;
public TimedHostedService(ILogger<TimedHostedService> logger)
{
_logger = logger;
}
public Task StartAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("Timed Hosted Service running.");
_timer = new Timer(DoWork, null, TimeSpan.Zero,
TimeSpan.FromSeconds(5));
return Task.CompletedTask;
}
private void DoWork(object state)
{
var count = Interlocked.Increment(ref executionCount);
_logger.LogInformation(
"Timed Hosted Service is working. Count: {Count}", count);
}
public Task StopAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("Timed Hosted Service is stopping.");
_timer?.Change(Timeout.Infinite, 0);
return Task.CompletedTask;
}
public void Dispose()
{
_timer?.Dispose();
}
}
前の DoWork
の実行が完了するまで Timer は待機されないため、ここで示したアプローチはすべてのシナリオに適しているとは限りません。The Timer doesn't wait for previous executions of DoWork
to finish, so the approach shown might not be suitable for every scenario. Interlocked.Increment は、アトミック操作として実行カウンターをインクリメントするために使用されされます。これにより、複数のスレッドによって executionCount
が同時に更新されなくなります。Interlocked.Increment is used to increment the execution counter as an atomic operation, which ensures that multiple threads don't update executionCount
concurrently.
サービスは、AddHostedService
拡張メソッドを使用して IHostBuilder.ConfigureServices
(Program.cs) に登録されます。The service is registered in IHostBuilder.ConfigureServices
(Program.cs) with the AddHostedService
extension method:
services.AddHostedService<TimedHostedService>();
バックグラウンド タスクでスコープ サービスを使用するConsuming a scoped service in a background task
BackgroundService 内でスコープ サービスを使用するには、スコープを作成します。To use scoped services within a BackgroundService, create a scope. 既定では、ホステッド サービスのスコープは作成されません。No scope is created for a hosted service by default.
バックグラウンド タスクのスコープ サービスには、バックグラウンド タスクのロジックが含まれています。The scoped background task service contains the background task's logic. 次に例を示します。In the following example:
- サービスは非同期です。The service is asynchronous.
DoWork
メソッドはTask
を返します。TheDoWork
method returns aTask
. デモンストレーションのために、10 秒の遅延がDoWork
メソッドで待機されます。For demonstration purposes, a delay of ten seconds is awaited in theDoWork
method. - ILogger がサービスに挿入されます。An ILogger is injected into the service.
internal interface IScopedProcessingService
{
Task DoWork(CancellationToken stoppingToken);
}
internal class ScopedProcessingService : IScopedProcessingService
{
private int executionCount = 0;
private readonly ILogger _logger;
public ScopedProcessingService(ILogger<ScopedProcessingService> logger)
{
_logger = logger;
}
public async Task DoWork(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
executionCount++;
_logger.LogInformation(
"Scoped Processing Service is working. Count: {Count}", executionCount);
await Task.Delay(10000, stoppingToken);
}
}
}
ホステッド サービスはスコープを作成してバックグラウンド タスクのスコープ サービスを解決し、DoWork
メソッドを呼び出します。The hosted service creates a scope to resolve the scoped background task service to call its DoWork
method. ExecuteAsync
で待機していた DoWork
が Task
を返します。DoWork
returns a Task
, which is awaited in ExecuteAsync
:
public class ConsumeScopedServiceHostedService : BackgroundService
{
private readonly ILogger<ConsumeScopedServiceHostedService> _logger;
public ConsumeScopedServiceHostedService(IServiceProvider services,
ILogger<ConsumeScopedServiceHostedService> logger)
{
Services = services;
_logger = logger;
}
public IServiceProvider Services { get; }
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogInformation(
"Consume Scoped Service Hosted Service running.");
await DoWork(stoppingToken);
}
private async Task DoWork(CancellationToken stoppingToken)
{
_logger.LogInformation(
"Consume Scoped Service Hosted Service is working.");
using (var scope = Services.CreateScope())
{
var scopedProcessingService =
scope.ServiceProvider
.GetRequiredService<IScopedProcessingService>();
await scopedProcessingService.DoWork(stoppingToken);
}
}
public override async Task StopAsync(CancellationToken stoppingToken)
{
_logger.LogInformation(
"Consume Scoped Service Hosted Service is stopping.");
await base.StopAsync(stoppingToken);
}
}
サービスは IHostBuilder.ConfigureServices
(Program.cs) に登録されています。The services are registered in IHostBuilder.ConfigureServices
(Program.cs). ホステッド サービスは、AddHostedService
拡張メソッドを使用して登録されます。The hosted service is registered with the AddHostedService
extension method:
services.AddHostedService<ConsumeScopedServiceHostedService>();
services.AddScoped<IScopedProcessingService, ScopedProcessingService>();
キューに格納されたバックグラウンド タスクQueued background tasks
バックグラウンド タスク キューは、.NET 4.x QueueBackgroundWorkItem に基づいています。A background task queue is based on the .NET 4.x QueueBackgroundWorkItem:
public interface IBackgroundTaskQueue
{
void QueueBackgroundWorkItem(Func<CancellationToken, Task> workItem);
Task<Func<CancellationToken, Task>> DequeueAsync(
CancellationToken cancellationToken);
}
public class BackgroundTaskQueue : IBackgroundTaskQueue
{
private ConcurrentQueue<Func<CancellationToken, Task>> _workItems =
new ConcurrentQueue<Func<CancellationToken, Task>>();
private SemaphoreSlim _signal = new SemaphoreSlim(0);
public void QueueBackgroundWorkItem(
Func<CancellationToken, Task> workItem)
{
if (workItem == null)
{
throw new ArgumentNullException(nameof(workItem));
}
_workItems.Enqueue(workItem);
_signal.Release();
}
public async Task<Func<CancellationToken, Task>> DequeueAsync(
CancellationToken cancellationToken)
{
await _signal.WaitAsync(cancellationToken);
_workItems.TryDequeue(out var workItem);
return workItem;
}
}
次の QueueHostedService
の例では以下のようになります。In the following QueueHostedService
example:
ExecuteAsync
で待機していたBackgroundProcessing
メソッドがTask
を返します。TheBackgroundProcessing
method returns aTask
, which is awaited inExecuteAsync
.BackgroundProcessing
で、キュー内のバックグラウンド タスクがデキューされ、実行されます。Background tasks in the queue are dequeued and executed inBackgroundProcessing
.- 作業項目が待機されてから、
StopAsync
でサービスが停止します。Work items are awaited before the service stops inStopAsync
.
public class QueuedHostedService : BackgroundService
{
private readonly ILogger<QueuedHostedService> _logger;
public QueuedHostedService(IBackgroundTaskQueue taskQueue,
ILogger<QueuedHostedService> logger)
{
TaskQueue = taskQueue;
_logger = logger;
}
public IBackgroundTaskQueue TaskQueue { get; }
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogInformation(
$"Queued Hosted Service is running.{Environment.NewLine}" +
$"{Environment.NewLine}Tap W to add a work item to the " +
$"background queue.{Environment.NewLine}");
await BackgroundProcessing(stoppingToken);
}
private async Task BackgroundProcessing(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
var workItem =
await TaskQueue.DequeueAsync(stoppingToken);
try
{
await workItem(stoppingToken);
}
catch (Exception ex)
{
_logger.LogError(ex,
"Error occurred executing {WorkItem}.", nameof(workItem));
}
}
}
public override async Task StopAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("Queued Hosted Service is stopping.");
await base.StopAsync(stoppingToken);
}
}
MonitorLoop
サービスは、w
キーが入力デバイスで選択されると常に、ホステッド サービスのためにタスクのエンキューを処理します。A MonitorLoop
service handles enqueuing tasks for the hosted service whenever the w
key is selected on an input device:
IBackgroundTaskQueue
がMonitorLoop
サービスに挿入されます。TheIBackgroundTaskQueue
is injected into theMonitorLoop
service.IBackgroundTaskQueue.QueueBackgroundWorkItem
が呼び出され、作業項目がエンキューされます。IBackgroundTaskQueue.QueueBackgroundWorkItem
is called to enqueue a work item.- 作業項目により、実行時間の長いバックグラウンド タスクがシミュレートされます。The work item simulates a long-running background task:
- 5 秒間の遅延が 3 回実行されます (
Task.Delay
)。Three 5-second delays are executed (Task.Delay
). - タスクが取り消された場合、
try-catch
ステートメントによって OperationCanceledException がトラップされます。Atry-catch
statement traps OperationCanceledException if the task is cancelled.
- 5 秒間の遅延が 3 回実行されます (
public class MonitorLoop
{
private readonly IBackgroundTaskQueue _taskQueue;
private readonly ILogger _logger;
private readonly CancellationToken _cancellationToken;
public MonitorLoop(IBackgroundTaskQueue taskQueue,
ILogger<MonitorLoop> logger,
IHostApplicationLifetime applicationLifetime)
{
_taskQueue = taskQueue;
_logger = logger;
_cancellationToken = applicationLifetime.ApplicationStopping;
}
public void StartMonitorLoop()
{
_logger.LogInformation("Monitor Loop is starting.");
// Run a console user input loop in a background thread
Task.Run(() => Monitor());
}
public void Monitor()
{
while (!_cancellationToken.IsCancellationRequested)
{
var keyStroke = Console.ReadKey();
if (keyStroke.Key == ConsoleKey.W)
{
// Enqueue a background work item
_taskQueue.QueueBackgroundWorkItem(async token =>
{
// Simulate three 5-second tasks to complete
// for each enqueued work item
int delayLoop = 0;
var guid = Guid.NewGuid().ToString();
_logger.LogInformation(
"Queued Background Task {Guid} is starting.", guid);
while (!token.IsCancellationRequested && delayLoop < 3)
{
try
{
await Task.Delay(TimeSpan.FromSeconds(5), token);
}
catch (OperationCanceledException)
{
// Prevent throwing if the Delay is cancelled
}
delayLoop++;
_logger.LogInformation(
"Queued Background Task {Guid} is running. " +
"{DelayLoop}/3", guid, delayLoop);
}
if (delayLoop == 3)
{
_logger.LogInformation(
"Queued Background Task {Guid} is complete.", guid);
}
else
{
_logger.LogInformation(
"Queued Background Task {Guid} was cancelled.", guid);
}
});
}
}
}
}
サービスは IHostBuilder.ConfigureServices
(Program.cs) に登録されています。The services are registered in IHostBuilder.ConfigureServices
(Program.cs). ホステッド サービスは、AddHostedService
拡張メソッドを使用して登録されます。The hosted service is registered with the AddHostedService
extension method:
services.AddSingleton<MonitorLoop>();
services.AddHostedService<QueuedHostedService>();
services.AddSingleton<IBackgroundTaskQueue, BackgroundTaskQueue>();
MonitorLoop
は Program.Main
で開始されます。MonitorLoop
is started in Program.Main
:
var monitorLoop = host.Services.GetRequiredService<MonitorLoop>();
monitorLoop.StartMonitorLoop();
ASP.NET Core では、バックグラウンド タスクを ホステッド サービス として実装することができます。In ASP.NET Core, background tasks can be implemented as hosted services. ホストされるサービスは、IHostedService インターフェイスを実装するバックグラウンド タスク ロジックを持つクラスです。A hosted service is a class with background task logic that implements the IHostedService interface. このトピックでは、3 つのホステッド サービスの例について説明します。This topic provides three hosted service examples:
- タイマーで実行されるバックグラウンド タスク。Background task that runs on a timer.
- スコープ サービスをアクティブ化するホステッド サービス。Hosted service that activates a scoped service. スコープ サービスは依存関係の挿入 (DI) を使用できますThe scoped service can use dependency injection (DI)
- 連続して実行される、キューに格納されたバックグラウンド タスク。Queued background tasks that run sequentially.
サンプル コードを表示またはダウンロードします (ダウンロード方法)。View or download sample code (how to download)
PackagePackage
Microsoft.AspNetCore.App メタパッケージを参照するか、Microsoft.Extensions.Hosting パッケージへのパッケージ参照を追加します。Reference the Microsoft.AspNetCore.App metapackage or add a package reference to the Microsoft.Extensions.Hosting package.
IHostedService インターフェイスIHostedService interface
ホステッド サービスでは、IHostedService インターフェイスを実装します。Hosted services implement the IHostedService interface. このインターフェイスは、ホストによって管理されるオブジェクトの 2 つのメソッドを定義します。The interface defines two methods for objects that are managed by the host:
StartAsync(CancellationToken):
StartAsync
には、バックグラウンド タスクを開始するロジックが含まれています。StartAsync(CancellationToken):StartAsync
contains the logic to start the background task. Web ホスト を使用する場合は、サーバーが起動し、IApplicationLifetime.ApplicationStarted がトリガーされた後で、StartAsync
が呼び出されます。When using the Web Host,StartAsync
is called after the server has started and IApplicationLifetime.ApplicationStarted is triggered. 汎用ホスト を使用する場合は、ApplicationStarted
がトリガーされる前にStartAsync
が呼び出されます。When using the Generic Host,StartAsync
is called beforeApplicationStarted
is triggered.StopAsync(CancellationToken):ホストが正常なシャットダウンを実行しているときにトリガーされます。StopAsync(CancellationToken): Triggered when the host is performing a graceful shutdown.
StopAsync
には、バックグラウンド タスクを終了するロジックが含まれています。StopAsync
contains the logic to end the background task. アンマネージ リソースを破棄するには、IDisposable とファイナライザー (デストラクター) を実装します。Implement IDisposable and finalizers (destructors) to dispose of any unmanaged resources.キャンセル トークンには、シャットダウン プロセスが正常に行われないことを示す、既定の 5 秒間のタイムアウトが含まれています。The cancellation token has a default five second timeout to indicate that the shutdown process should no longer be graceful. キャンセルがトークンに要求された場合:When cancellation is requested on the token:
- アプリで実行されている残りのバックグラウンド操作が中止します。Any remaining background operations that the app is performing should be aborted.
StopAsync
で呼び出されたすべてのメソッドが速やかに戻ります。Any methods called inStopAsync
should return promptly.
ただし、キャンセルが要求された後もタスクは破棄されません—呼び出し元がすべてのタスクの完了を待機します。However, tasks aren't abandoned after cancellation is requested—the caller awaits all tasks to complete.
アプリが予期せずシャットダウンした場合 (たとえば、アプリのプロセスが失敗した場合)、
StopAsync
は呼び出されないことがあります。If the app shuts down unexpectedly (for example, the app's process fails),StopAsync
might not be called. そのため、StopAsync
で呼び出されたメソッドや行われた操作が実行されない可能性があります。Therefore, any methods called or operations conducted inStopAsync
might not occur.既定の 5 秒のシャットダウン タイムアウトを延長するには、次を設定します。To extend the default five second shutdown timeout, set:
- ShutdownTimeout (汎用ホストを使用するとき)ShutdownTimeout when using Generic Host. 詳細については、「ASP.NET Core の .NET 汎用ホスト」を参照してください。For more information, see ASP.NET Core の .NET 汎用ホスト.
- シャットダウン タイムアウトのホスト構成設定 (Web ホストを使用するとき)Shutdown timeout host configuration setting when using Web Host. 詳細については、「ASP.NET Core の Web ホスト」を参照してください。For more information, see ASP.NET Core の Web ホスト.
ホステッド サービスは、アプリの起動時に一度アクティブ化され、アプリのシャットダウン時に正常にシャットダウンされます。The hosted service is activated once at app startup and gracefully shut down at app shutdown. バックグラウンド タスクの実行中にエラーがスローされた場合、StopAsync
が呼び出されていなくても Dispose
を呼び出す必要があります。If an error is thrown during background task execution, Dispose
should be called even if StopAsync
isn't called.
時間が指定されたバックグラウンド タスクTimed background tasks
時間が指定されたバックグラウンド タスクは、System.Threading.Timer クラスを利用します。A timed background task makes use of the System.Threading.Timer class. このタイマーはタスクの DoWork
メソッドをトリガーします。The timer triggers the task's DoWork
method. タイマーは StopAsync
で無効になり、Dispose
でサービス コンテナーが破棄されたときに破棄されます。The timer is disabled on StopAsync
and disposed when the service container is disposed on Dispose
:
internal class TimedHostedService : IHostedService, IDisposable
{
private readonly ILogger _logger;
private Timer _timer;
public TimedHostedService(ILogger<TimedHostedService> logger)
{
_logger = logger;
}
public Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Timed Background Service is starting.");
_timer = new Timer(DoWork, null, TimeSpan.Zero,
TimeSpan.FromSeconds(5));
return Task.CompletedTask;
}
private void DoWork(object state)
{
_logger.LogInformation("Timed Background Service is working.");
}
public Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Timed Background Service is stopping.");
_timer?.Change(Timeout.Infinite, 0);
return Task.CompletedTask;
}
public void Dispose()
{
_timer?.Dispose();
}
}
前の DoWork
の実行が完了するまで Timer は待機されないため、ここで示したアプローチはすべてのシナリオに適しているとは限りません。The Timer doesn't wait for previous executions of DoWork
to finish, so the approach shown might not be suitable for every scenario.
サービスは、AddHostedService
拡張メソッドを使用して Startup.ConfigureServices
に登録されます。The service is registered in Startup.ConfigureServices
with the AddHostedService
extension method:
services.AddHostedService<TimedHostedService>();
バックグラウンド タスクでスコープ サービスを使用するConsuming a scoped service in a background task
IHostedService
内でスコープ サービスを使用するには、スコープを作成します。To use scoped services within an IHostedService
, create a scope. 既定では、ホステッド サービスのスコープは作成されません。No scope is created for a hosted service by default.
バックグラウンド タスクのスコープ サービスには、バックグラウンド タスクのロジックが含まれています。The scoped background task service contains the background task's logic. 次の例では、ILogger がサービスに挿入されています。In the following example, an ILogger is injected into the service:
internal interface IScopedProcessingService
{
void DoWork();
}
internal class ScopedProcessingService : IScopedProcessingService
{
private readonly ILogger _logger;
public ScopedProcessingService(ILogger<ScopedProcessingService> logger)
{
_logger = logger;
}
public void DoWork()
{
_logger.LogInformation("Scoped Processing Service is working.");
}
}
ホステッド サービスはスコープを作成してバックグラウンド タスクのスコープ サービスを解決し、DoWork
メソッドを呼び出します。The hosted service creates a scope to resolve the scoped background task service to call its DoWork
method:
internal class ConsumeScopedServiceHostedService : IHostedService
{
private readonly ILogger _logger;
public ConsumeScopedServiceHostedService(IServiceProvider services,
ILogger<ConsumeScopedServiceHostedService> logger)
{
Services = services;
_logger = logger;
}
public IServiceProvider Services { get; }
public Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation(
"Consume Scoped Service Hosted Service is starting.");
DoWork();
return Task.CompletedTask;
}
private void DoWork()
{
_logger.LogInformation(
"Consume Scoped Service Hosted Service is working.");
using (var scope = Services.CreateScope())
{
var scopedProcessingService =
scope.ServiceProvider
.GetRequiredService<IScopedProcessingService>();
scopedProcessingService.DoWork();
}
}
public Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation(
"Consume Scoped Service Hosted Service is stopping.");
return Task.CompletedTask;
}
}
サービスは Startup.ConfigureServices
に登録されています。The services are registered in Startup.ConfigureServices
. IHostedService
の実装は、AddHostedService
拡張メソッドで登録されます。The IHostedService
implementation is registered with the AddHostedService
extension method:
services.AddHostedService<ConsumeScopedServiceHostedService>();
services.AddScoped<IScopedProcessingService, ScopedProcessingService>();
キューに格納されたバックグラウンド タスクQueued background tasks
バックグラウンド タスク キューは、.NET Framework 4.x QueueBackgroundWorkItem (暫定的に ASP.NET Core に組み込まれる予定) に基づいています。A background task queue is based on the .NET Framework 4.x QueueBackgroundWorkItem (tentatively scheduled to be built-in for ASP.NET Core):
public interface IBackgroundTaskQueue
{
void QueueBackgroundWorkItem(Func<CancellationToken, Task> workItem);
Task<Func<CancellationToken, Task>> DequeueAsync(
CancellationToken cancellationToken);
}
public class BackgroundTaskQueue : IBackgroundTaskQueue
{
private ConcurrentQueue<Func<CancellationToken, Task>> _workItems =
new ConcurrentQueue<Func<CancellationToken, Task>>();
private SemaphoreSlim _signal = new SemaphoreSlim(0);
public void QueueBackgroundWorkItem(
Func<CancellationToken, Task> workItem)
{
if (workItem == null)
{
throw new ArgumentNullException(nameof(workItem));
}
_workItems.Enqueue(workItem);
_signal.Release();
}
public async Task<Func<CancellationToken, Task>> DequeueAsync(
CancellationToken cancellationToken)
{
await _signal.WaitAsync(cancellationToken);
_workItems.TryDequeue(out var workItem);
return workItem;
}
}
QueueHostedService
では、キュー内のバックグラウンド タスクは BackgroundService としてデキューされ、実行されます。これは、長時間実行する IHostedService
を構成するための基本クラスです。In QueueHostedService
, background tasks in the queue are dequeued and executed as a BackgroundService, which is a base class for implementing a long running IHostedService
:
public class QueuedHostedService : BackgroundService
{
private readonly ILogger _logger;
public QueuedHostedService(IBackgroundTaskQueue taskQueue,
ILoggerFactory loggerFactory)
{
TaskQueue = taskQueue;
_logger = loggerFactory.CreateLogger<QueuedHostedService>();
}
public IBackgroundTaskQueue TaskQueue { get; }
protected async override Task ExecuteAsync(
CancellationToken cancellationToken)
{
_logger.LogInformation("Queued Hosted Service is starting.");
while (!cancellationToken.IsCancellationRequested)
{
var workItem = await TaskQueue.DequeueAsync(cancellationToken);
try
{
await workItem(cancellationToken);
}
catch (Exception ex)
{
_logger.LogError(ex,
"Error occurred executing {WorkItem}.", nameof(workItem));
}
}
_logger.LogInformation("Queued Hosted Service is stopping.");
}
}
サービスは Startup.ConfigureServices
に登録されています。The services are registered in Startup.ConfigureServices
. IHostedService
の実装は、AddHostedService
拡張メソッドで登録されます。The IHostedService
implementation is registered with the AddHostedService
extension method:
services.AddHostedService<QueuedHostedService>();
services.AddSingleton<IBackgroundTaskQueue, BackgroundTaskQueue>();
インデックス ページ モデル クラスで:In the Index page model class:
IBackgroundTaskQueue
がコンストラクターに挿入され、Queue
に割り当てられます。TheIBackgroundTaskQueue
is injected into the constructor and assigned toQueue
.- IServiceScopeFactory が挿入され、
_serviceScopeFactory
に割り当てられます。An IServiceScopeFactory is injected and assigned to_serviceScopeFactory
. ファクトリは、スコープ内でサービス作成するための IServiceScope のインスタンス作成に使用されます。The factory is used to create instances of IServiceScope, which is used to create services within a scope. スコープは、アプリのAppDbContext
(スコープ サービス) を使用し、データベース レコードをIBackgroundTaskQueue
(シングルトン サービス) に書き込むために作成されます。A scope is created in order to use the app'sAppDbContext
(a scoped service) to write database records in theIBackgroundTaskQueue
(a singleton service).
public class IndexModel : PageModel
{
private readonly AppDbContext _db;
private readonly ILogger _logger;
private readonly IServiceScopeFactory _serviceScopeFactory;
public IndexModel(AppDbContext db, IBackgroundTaskQueue queue,
ILogger<IndexModel> logger, IServiceScopeFactory serviceScopeFactory)
{
_db = db;
_logger = logger;
Queue = queue;
_serviceScopeFactory = serviceScopeFactory;
}
public IBackgroundTaskQueue Queue { get; }
[インデックス] ページで [タスクの追加] ボタンを選択すると、OnPostAddTask
メソッドが実行されます。When the Add Task button is selected on the Index page, the OnPostAddTask
method is executed. QueueBackgroundWorkItem
が呼び出され、作業項目がエンキューされます。QueueBackgroundWorkItem
is called to enqueue a work item:
public IActionResult OnPostAddTaskAsync()
{
Queue.QueueBackgroundWorkItem(async token =>
{
var guid = Guid.NewGuid().ToString();
using (var scope = _serviceScopeFactory.CreateScope())
{
var scopedServices = scope.ServiceProvider;
var db = scopedServices.GetRequiredService<AppDbContext>();
for (int delayLoop = 1; delayLoop < 4; delayLoop++)
{
try
{
db.Messages.Add(
new Message()
{
Text = $"Queued Background Task {guid} has " +
$"written a step. {delayLoop}/3"
});
await db.SaveChangesAsync();
}
catch (Exception ex)
{
_logger.LogError(ex,
"An error occurred writing to the " +
"database. Error: {Message}", ex.Message);
}
await Task.Delay(TimeSpan.FromSeconds(5), token);
}
}
_logger.LogInformation(
"Queued Background Task {Guid} is complete. 3/3", guid);
});
return RedirectToPage();
}