title | author | description | monikerRange | ms.author | ms.custom | ms.date | uid |
---|---|---|---|---|---|---|---|
Background tasks with hosted services in ASP.NET Core |
tdykstra |
Learn how to implement background tasks with hosted services in ASP.NET Core. |
>= aspnetcore-3.1 |
tdykstra |
mvc |
5/10/2024 |
fundamentals/host/hosted-services |
By Jeow Li Huan
:::moniker range=">= aspnetcore-8.0"
In ASP.NET Core, background tasks can be implemented as hosted services. A hosted service is a class with background task logic that implements the xref:Microsoft.Extensions.Hosting.IHostedService interface. This article provides three hosted service examples:
- Background task that runs on a timer.
- Hosted service that activates a scoped service. The scoped service can use dependency injection (DI).
- Queued background tasks that run sequentially.
The ASP.NET Core Worker Service template provides a starting point for writing long running service apps. 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:
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. For example, see the sample app's project file (BackgroundTasksSample.csproj
).
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.
The xref:Microsoft.Extensions.Hosting.IHostedService interface defines two methods for objects that are managed by the host:
- StartAsync(CancellationToken)
- StopAsync(CancellationToken)
StartAsync(CancellationToken) contains the logic to start the background task. StartAsync
is called before:
- The app's request processing pipeline is configured.
- The server is started and IApplicationLifetime.ApplicationStarted is triggered.
StartAsync
should be limited to short running tasks because hosted services are run sequentially, and no further services are started until StartAsync
runs to completion.
- StopAsync(CancellationToken) is triggered when the host is performing a graceful shutdown.
StopAsync
contains the logic to end the background task. Implement xref:System.IDisposable and finalizers (destructors) to dispose of any unmanaged resources.
The cancellation token has a default 30 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.
- Any methods called in
StopAsync
should return promptly.
However, tasks aren't abandoned after cancellation is requested—the caller awaits all tasks to complete.
If the app shuts down unexpectedly (for example, the app's process fails), StopAsync
might not be called. Therefore, any methods called or operations conducted in StopAsync
might not occur.
To extend the default 30 second shutdown timeout, set:
- xref:Microsoft.Extensions.Hosting.HostOptions.ShutdownTimeout%2A when using Generic Host. For more information, see xref:fundamentals/host/generic-host#shutdowntimeout.
- Shutdown timeout host configuration setting when using Web Host. For more information, see xref:fundamentals/host/web-host#shutdown-timeout.
The hosted service is activated once at app startup and gracefully shut down at app shutdown. If an error is thrown during background task execution, Dispose
should be called even if StopAsync
isn't called.
xref:Microsoft.Extensions.Hosting.BackgroundService is a base class for implementing a long running xref:Microsoft.Extensions.Hosting.IHostedService.
ExecuteAsync(CancellationToken) is called to run the background service. The implementation returns a xref:System.Threading.Tasks.Task that represents the entire lifetime of the background service. No further services are started until ExecuteAsync becomes asynchronous, such as by calling await
. Avoid performing long, blocking initialization work in ExecuteAsync
. The host blocks in StopAsync(CancellationToken) waiting for ExecuteAsync
to complete.
The cancellation token is triggered when IHostedService.StopAsync is called. 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. For more information, see the IHostedService interface section.
For more information, see the BackgroundService source code.
A timed background task makes use of the System.Threading.Timer class. The timer triggers the task's DoWork
method. The timer is disabled on StopAsync
and disposed when the service container is disposed on Dispose
:
:::code language="csharp" source="~/fundamentals/host/hosted-services/samples/6.0/BackgroundTasksSample/Services/TimedHostedService.cs" id="snippet1" highlight="16-17,34,41":::
The xref:System.Threading.Timer doesn't wait for previous executions of DoWork
to finish, so the approach shown might not be suitable for every scenario. Interlocked.Increment is used to increment the execution counter as an atomic operation, which ensures that multiple threads don't update executionCount
concurrently.
The service is registered in IHostBuilder.ConfigureServices
(Program.cs
) with the AddHostedService
extension method:
:::code language="csharp" source="~/fundamentals/host/hosted-services/samples/6.0/BackgroundTasksSample/Program.cs" id="snippet1":::
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. The
DoWork
method returns aTask
. For demonstration purposes, a delay of ten seconds is awaited in theDoWork
method. - An xref:Microsoft.Extensions.Logging.ILogger is injected into the service.
:::code language="csharp" source="~/fundamentals/host/hosted-services/samples/6.0/BackgroundTasksSample/Services/ScopedProcessingService.cs" id="snippet1":::
The hosted service creates a scope to resolve the scoped background task service to call its DoWork
method. DoWork
returns a Task
, which is awaited in ExecuteAsync
:
:::code language="csharp" source="~/fundamentals/host/hosted-services/samples/6.0/BackgroundTasksSample/Services/ConsumeScopedServiceHostedService.cs" id="snippet1" highlight="19,22-35":::
The services are registered in IHostBuilder.ConfigureServices
(Program.cs
). The hosted service is registered with the AddHostedService
extension method:
:::code language="csharp" source="~/fundamentals/host/hosted-services/samples/3.x/BackgroundTasksSample/Program.cs" id="snippet2":::
A background task queue is based on the .NET 4.x xref:System.Web.Hosting.HostingEnvironment.QueueBackgroundWorkItem%2A:
:::code language="csharp" source="~/fundamentals/host/hosted-services/samples/6.0/BackgroundTasksSample/Services/BackgroundTaskQueue.cs" id="snippet1":::
In the following QueueHostedService
example:
- The
BackgroundProcessing
method returns aTask
, which is awaited inExecuteAsync
. - Background tasks in the queue are dequeued and executed in
BackgroundProcessing
. - Work items are awaited before the service stops in
StopAsync
.
:::code language="csharp" source="~/fundamentals/host/hosted-services/samples/6.0/BackgroundTasksSample/Services/QueuedHostedService.cs" id="snippet1" highlight="28-29,33":::
A MonitorLoop
service handles enqueuing tasks for the hosted service whenever the w
key is selected on an input device:
- The
IBackgroundTaskQueue
is injected into theMonitorLoop
service. IBackgroundTaskQueue.QueueBackgroundWorkItem
is called to enqueue a work item.- The work item simulates a long-running background task:
- Three 5-second delays are executed (
Task.Delay
). - A
try-catch
statement traps xref:System.OperationCanceledException if the task is cancelled.
- Three 5-second delays are executed (
:::code language="csharp" source="~/fundamentals/host/hosted-services/samples/6.0/BackgroundTasksSample/Services/MonitorLoop.cs" id="snippet_Monitor" highlight="7,33":::
The services are registered in IHostBuilder.ConfigureServices
(Program.cs
). The hosted service is registered with the AddHostedService
extension method:
:::code language="csharp" source="~/fundamentals/host/hosted-services/samples/6.0/BackgroundTasksSample/Program.cs" id="snippet3":::
MonitorLoop
is started in Program.cs
:
:::code language="csharp" source="~/fundamentals/host/hosted-services/samples/6.0/BackgroundTasksSample/Program.cs" id="snippet4":::
The following code creates an asynchronous timed background task:
:::code language="csharp" source="~/../AspNetCore.Docs.Samples/fundamentals/host/TimedBackgroundTasks/TimedHostedService.cs":::
The Worker Service templates support .NET native ahead-of-time (AOT) with the --aot
flag:
- 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 Next.
- In the Additional information dialog:
- Choose a Framework.
- Check the Enable Native AOT publish checkbox.
- Select Create.
Use the Worker Service (worker
) template with the dotnet new command from a command shell with the AOT option:
dotnet new worker -o WorkerWithAot --aot
The AOT option adds <PublishAot>true</PublishAot>
to the project file:
<Project Sdk="Microsoft.NET.Sdk.Worker">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<InvariantGlobalization>true</InvariantGlobalization>
+ <PublishAot>true</PublishAot>
<UserSecretsId>dotnet-WorkerWithAot-e94b2</UserSecretsId>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0-preview.4.23259.5" />
</ItemGroup>
</Project>
- Background services unit tests on GitHub.
- View or download sample code (how to download)
- Implement background tasks in microservices with IHostedService and the BackgroundService class
- Run background tasks with WebJobs in Azure App Service
- xref:System.Threading.Timer
:::moniker-end