Tâches d’arrière-plan avec des services hébergés dans ASP.NET CoreBackground tasks with hosted services in ASP.NET Core
Par Jeow Li HuanBy Jeow Li Huan
Dans ASP.NET Core, les tâches d’arrière-plan peuvent être implémentées en tant que services hébergés.In ASP.NET Core, background tasks can be implemented as hosted services. Un service hébergé est une classe avec la logique de tâches en arrière-plan qui implémente l’interface IHostedService.A hosted service is a class with background task logic that implements the IHostedService interface. Cette rubrique contient trois exemples de service hébergé :This topic provides three hosted service examples:
- Tâche d’arrière-plan qui s’exécute sur un minuteur.Background task that runs on a timer.
- Service hébergé qui active un service étendu.Hosted service that activates a scoped service. Le service étendu peut utiliser l' injection de dépendances (di).The scoped service can use dependency injection (DI).
- Tâches d’arrière-plan en file d’attente qui s’exécutent séquentiellement.Queued background tasks that run sequentially.
Afficher ou télécharger l’exemple de code (procédure de téléchargement)View or download sample code (how to download)
Modèle Service WorkerWorker Service template
Le modèle Service Worker ASP.NET Core fournit un point de départ pour l’écriture d’applications de service durables.The ASP.NET Core Worker Service template provides a starting point for writing long running service apps. Une application créée à partir du modèle de service Worker spécifie le kit de développement logiciel (SDK) Worker dans son fichier projet :An app created from the Worker Service template specifies the Worker SDK in its project file:
<Project Sdk="Microsoft.NET.Sdk.Worker">
Pour utiliser le modèle en tant que base d’une application de services hébergés :To use the template as a basis for a hosted services app:
- Créez un projet.Create a new project.
- Sélectionnez Worker service.Select Worker Service. Sélectionnez Suivant.Select Next.
- Indiquez un nom de projet dans le champ Nom du projet, ou acceptez le nom de projet par défaut.Provide a project name in the Project name field or accept the default project name. Sélectionnez Create (Créer).Select Create.
- Dans la boîte de dialogue créer un service de travail , sélectionnez créer.In the Create a new Worker service dialog, select Create.
PaquetPackage
Une application basée sur le modèle de service worker utilise le Microsoft.NET.Sdk.Worker
Kit de développement logiciel (SDK) et possède une référence de package explicite au package 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. Par exemple, consultez le fichier projet de l’exemple d’application (BackgroundTasksSample. csproj).For example, see the sample app's project file (BackgroundTasksSample.csproj).
Pour les applications Web qui utilisent le Microsoft.NET.Sdk.Web
Kit de développement logiciel (SDK), le package Microsoft. extensions. Hosting est référencé implicitement à partir de l’infrastructure partagée.For web apps that use the Microsoft.NET.Sdk.Web
SDK, the Microsoft.Extensions.Hosting package is referenced implicitly from the shared framework. Une référence de package explicite dans le fichier projet de l’application n’est pas obligatoire.An explicit package reference in the app's project file isn't required.
Interface IHostedServiceIHostedService interface
L' IHostedService interface définit deux méthodes pour les objets gérés par l’hôte :The IHostedService interface defines two methods for objects that are managed by the host:
StartAsync (CancellationToken):
StartAsync
contient la logique de démarrage de la tâche en arrière-plan.StartAsync(CancellationToken):StartAsync
contains the logic to start the background task.StartAsync
est appelé avant:StartAsync
is called before:- Le pipeline de traitement des demandes de l’application est configuré (
Startup.Configure
).The app's request processing pipeline is configured (Startup.Configure
). - Le serveur est démarré et IApplicationLifetime. ApplicationStarted est déclenché.The server is started and IApplicationLifetime.ApplicationStarted is triggered.
Le comportement par défaut peut être modifié de sorte que le service hébergé
StartAsync
s’exécute après que le pipeline de l’application a été configuré et qu’ilApplicationStarted
est appelé.The default behavior can be changed so that the hosted service'sStartAsync
runs after the app's pipeline has been configured andApplicationStarted
is called. Pour modifier le comportement par défaut, ajoutez le service hébergé (VideosWatcher
dans l’exemple suivant) après l’appel deConfigureWebHostDefaults
: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>(); }); }
- Le pipeline de traitement des demandes de l’application est configuré (
StopAsync (CancellationToken): déclenché lorsque l’hôte effectue un arrêt approprié.StopAsync(CancellationToken): Triggered when the host is performing a graceful shutdown.
StopAsync
contient la logique pour terminer la tâche d’arrière-plan.StopAsync
contains the logic to end the background task. Implémentez IDisposable et les finaliseurs (destructeurs) pour supprimer toutes les ressources non managées.Implement IDisposable and finalizers (destructors) to dispose of any unmanaged resources.Le jeton d’annulation a un délai d’expiration par défaut de cinq secondes pour indiquer que le processus d’arrêt ne doit plus être normal.The cancellation token has a default five second timeout to indicate that the shutdown process should no longer be graceful. Quand l’annulation est demandée sur le jeton :When cancellation is requested on the token:
- Les opérations en arrière-plan restantes effectuées par l’application doivent être abandonnées.Any remaining background operations that the app is performing should be aborted.
- Les méthodes appelées dans
StopAsync
doivent retourner rapidement.Any methods called inStopAsync
should return promptly.
Cependant, les tâches ne sont pas abandonnées après la demande d’annulation : l’appelant attend que toutes les tâches se terminent.However, tasks aren't abandoned after cancellation is requested—the caller awaits all tasks to complete.
Si l’application s’arrête inopinément (par exemple en cas d’échec du processus de l’application),
StopAsync
n’est probablement pas appelée.If the app shuts down unexpectedly (for example, the app's process fails),StopAsync
might not be called. Par conséquent, les méthodes appelées ou les opérations effectuées dansStopAsync
peuvent ne pas se produire.Therefore, any methods called or operations conducted inStopAsync
might not occur.Pour prolonger le délai d’expiration par défaut de cinq secondes, définissez :To extend the default five second shutdown timeout, set:
- ShutdownTimeout quand vous utilisez l’hôte générique.ShutdownTimeout when using Generic Host. Pour plus d’informations, consultez Hôte générique .NET dans ASP.NET Core.For more information, see Hôte générique .NET dans ASP.NET Core.
- Le paramètre de configuration du délai d’expiration de l’hôte quand vous utilisez l’hôte web.Shutdown timeout host configuration setting when using Web Host. Pour plus d’informations, consultez Hôte web ASP.NET Core.For more information, see Hôte web ASP.NET Core.
Le service hébergé est activé une seule fois au démarrage de l’application et s’arrête normalement à l’arrêt de l’application.The hosted service is activated once at app startup and gracefully shut down at app shutdown. Si une erreur est levée pendant l’exécution des tâches d’arrière-plan, Dispose
doit être appelée même si StopAsync
n’est pas appelée.If an error is thrown during background task execution, Dispose
should be called even if StopAsync
isn't called.
Classe de base BackgroundServiceBackgroundService base class
BackgroundService est une classe de base pour l’implémentation d’une exécution longue IHostedService .BackgroundService is a base class for implementing a long running IHostedService.
ExecuteAsync (CancellationToken) est appelé pour exécuter le service d’arrière-plan.ExecuteAsync(CancellationToken) is called to run the background service. L’implémentation retourne un Task qui représente la durée de vie totale du service d’arrière-plan.The implementation returns a Task that represents the entire lifetime of the background service. Aucun autre service n’est démarré tant que ExecuteAsync n’est pas asynchrone, par exemple en appelant await
.No further services are started until ExecuteAsync becomes asynchronous, such as by calling await
. Évitez d’effectuer des opérations d’initialisation de blocage longues dans ExecuteAsync
.Avoid performing long, blocking initialization work in ExecuteAsync
. L’hôte bloque dans StopAsync (CancellationToken) en attente de la fin de l' ExecuteAsync
exécution.The host blocks in StopAsync(CancellationToken) waiting for ExecuteAsync
to complete.
Le jeton d’annulation est déclenché lors de l’appel de IHostedService. StopAsync .The cancellation token is triggered when IHostedService.StopAsync is called. Votre implémentation de ExecuteAsync
doit se terminer rapidement lorsque le jeton d’annulation est déclenché afin d’arrêter correctement le service.Your implementation of ExecuteAsync
should finish promptly when the cancellation token is fired in order to gracefully shut down the service. Dans le cas contraire, le service s’arrête de manière inappropriée au délai d’attente de l’arrêt.Otherwise, the service ungracefully shuts down at the shutdown timeout. Pour plus d’informations, consultez la section de l' interface IHostedService .For more information, see the IHostedService interface section.
Tâche d’arrière-plan avec minuteurTimed background tasks
Une tâche d’arrière-plan avec minuteur utilise la classe System.Threading.Timer.A timed background task makes use of the System.Threading.Timer class. Le minuteur déclenche la méthode DoWork
de la tâche.The timer triggers the task's DoWork
method. Le minuteur est désactivé sur StopAsync
et supprimé quand le conteneur du service est supprimé sur 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();
}
}
Le Timer n’attend pas que les exécutions précédentes se DoWork
terminent. l’approche illustrée peut donc ne pas convenir à chaque scénario.The Timer doesn't wait for previous executions of DoWork
to finish, so the approach shown might not be suitable for every scenario. Avec verrouillage. l’incrément est utilisé pour incrémenter le compteur d’exécution comme une opération atomique, ce qui garantit que plusieurs threads ne sont pas mis à jour executionCount
simultanément.Interlocked.Increment is used to increment the execution counter as an atomic operation, which ensures that multiple threads don't update executionCount
concurrently.
Le service est inscrit dans IHostBuilder.ConfigureServices
(Program.cs) avec la AddHostedService
méthode d’extension :The service is registered in IHostBuilder.ConfigureServices
(Program.cs) with the AddHostedService
extension method:
services.AddHostedService<TimedHostedService>();
Utilisation d’un service délimité dans une tâche d’arrière-planConsuming a scoped service in a background task
Pour utiliser les services délimités dans un BackgroundService, créez une étendue.To use scoped services within a BackgroundService, create a scope. Par défaut, aucune étendue n’est créée pour un service hébergé.No scope is created for a hosted service by default.
Le service des tâches d’arrière-plan délimitées contient la logique de la tâche d’arrière-plan.The scoped background task service contains the background task's logic. Dans l’exemple suivant :In the following example:
- Le service est asynchrone.The service is asynchronous. La méthode
DoWork
retourne unTask
.TheDoWork
method returns aTask
. À des fins de démonstration, un délai de dix secondes est attendu dans laDoWork
méthode.For demonstration purposes, a delay of ten seconds is awaited in theDoWork
method. - Un ILogger est injecté dans le service.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);
}
}
}
Le service hébergé crée une étendue pour résoudre le service de tâche d’arrière-plan étendu afin d’appeler sa DoWork
méthode.The hosted service creates a scope to resolve the scoped background task service to call its DoWork
method. DoWork
retourne un Task
, qui est attendu dans ExecuteAsync
: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);
}
}
Les services sont inscrits dans IHostBuilder.ConfigureServices
(Program.cs).The services are registered in IHostBuilder.ConfigureServices
(Program.cs). Le service hébergé est inscrit avec la AddHostedService
méthode d’extension :The hosted service is registered with the AddHostedService
extension method:
services.AddHostedService<ConsumeScopedServiceHostedService>();
services.AddScoped<IScopedProcessingService, ScopedProcessingService>();
Tâches d’arrière-plan en file d’attenteQueued background tasks
Une file d’attente de tâches en arrière-plan est basée sur .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;
}
}
Dans l' QueueHostedService
exemple suivant :In the following QueueHostedService
example:
- La
BackgroundProcessing
méthode retourne unTask
, qui est attendu dansExecuteAsync
.TheBackgroundProcessing
method returns aTask
, which is awaited inExecuteAsync
. - Les tâches en arrière-plan de la file d’attente sont déplacées en file d’attente et exécutées dans
BackgroundProcessing
.Background tasks in the queue are dequeued and executed inBackgroundProcessing
. - Les éléments de travail sont attendus avant l’arrêt du service dans
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);
}
}
Un MonitorLoop
service gère les tâches de mise en file d’attente pour le service hébergé chaque fois que la w
clé est sélectionnée sur un appareil d’entrée :A MonitorLoop
service handles enqueuing tasks for the hosted service whenever the w
key is selected on an input device:
IBackgroundTaskQueue
Est injecté dans leMonitorLoop
service.TheIBackgroundTaskQueue
is injected into theMonitorLoop
service.IBackgroundTaskQueue.QueueBackgroundWorkItem
est appelé pour empiler un élément de travail.IBackgroundTaskQueue.QueueBackgroundWorkItem
is called to enqueue a work item.- L’élément de travail simule une tâche en arrière-plan de longue durée :The work item simulates a long-running background task:
- Les retards de 3 5 seconde sont exécutés (
Task.Delay
).Three 5-second delays are executed (Task.Delay
). - Une
try-catch
instruction intercepte OperationCanceledException si la tâche est annulée.Atry-catch
statement traps OperationCanceledException if the task is cancelled.
- Les retards de 3 5 seconde sont exécutés (
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);
}
});
}
}
}
}
Les services sont inscrits dans IHostBuilder.ConfigureServices
(Program.cs).The services are registered in IHostBuilder.ConfigureServices
(Program.cs). Le service hébergé est inscrit avec la AddHostedService
méthode d’extension :The hosted service is registered with the AddHostedService
extension method:
services.AddSingleton<MonitorLoop>();
services.AddHostedService<QueuedHostedService>();
services.AddSingleton<IBackgroundTaskQueue, BackgroundTaskQueue>();
MonitorLoop
est démarré dans Program.Main
:MonitorLoop
is started in Program.Main
:
var monitorLoop = host.Services.GetRequiredService<MonitorLoop>();
monitorLoop.StartMonitorLoop();
Dans ASP.NET Core, les tâches d’arrière-plan peuvent être implémentées en tant que services hébergés.In ASP.NET Core, background tasks can be implemented as hosted services. Un service hébergé est une classe avec la logique de tâches en arrière-plan qui implémente l’interface IHostedService.A hosted service is a class with background task logic that implements the IHostedService interface. Cette rubrique contient trois exemples de service hébergé :This topic provides three hosted service examples:
- Tâche d’arrière-plan qui s’exécute sur un minuteur.Background task that runs on a timer.
- Service hébergé qui active un service étendu.Hosted service that activates a scoped service. Le service étendu peut utiliser l' injection de dépendances (di)The scoped service can use dependency injection (DI)
- Tâches d’arrière-plan en file d’attente qui s’exécutent séquentiellement.Queued background tasks that run sequentially.
Afficher ou télécharger l’exemple de code (procédure de téléchargement)View or download sample code (how to download)
PaquetPackage
Référencer le métapackage Microsoft.AspNetCore.App ou ajouter une référence de package au package Microsoft.Extensions.Hosting.Reference the Microsoft.AspNetCore.App metapackage or add a package reference to the Microsoft.Extensions.Hosting package.
Interface IHostedServiceIHostedService interface
Les services hébergés implémentent l’interface IHostedService.Hosted services implement the IHostedService interface. L’interface définit deux méthodes pour les objets qui sont gérés par l’hôte :The interface defines two methods for objects that are managed by the host:
StartAsync (CancellationToken):
StartAsync
contient la logique de démarrage de la tâche en arrière-plan.StartAsync(CancellationToken):StartAsync
contains the logic to start the background task. Lorsque vous utilisez l' hôte Web,StartAsync
est appelé après que le serveur a démarré et IApplicationLifetime. ApplicationStarted est déclenché.When using the Web Host,StartAsync
is called after the server has started and IApplicationLifetime.ApplicationStarted is triggered. Lors de l’utilisation de l' hôte générique,StartAsync
est appelé avant queApplicationStarted
ne soit déclenché.When using the Generic Host,StartAsync
is called beforeApplicationStarted
is triggered.StopAsync (CancellationToken): déclenché lorsque l’hôte effectue un arrêt approprié.StopAsync(CancellationToken): Triggered when the host is performing a graceful shutdown.
StopAsync
contient la logique pour terminer la tâche d’arrière-plan.StopAsync
contains the logic to end the background task. Implémentez IDisposable et les finaliseurs (destructeurs) pour supprimer toutes les ressources non managées.Implement IDisposable and finalizers (destructors) to dispose of any unmanaged resources.Le jeton d’annulation a un délai d’expiration par défaut de cinq secondes pour indiquer que le processus d’arrêt ne doit plus être normal.The cancellation token has a default five second timeout to indicate that the shutdown process should no longer be graceful. Quand l’annulation est demandée sur le jeton :When cancellation is requested on the token:
- Les opérations en arrière-plan restantes effectuées par l’application doivent être abandonnées.Any remaining background operations that the app is performing should be aborted.
- Les méthodes appelées dans
StopAsync
doivent retourner rapidement.Any methods called inStopAsync
should return promptly.
Cependant, les tâches ne sont pas abandonnées après la demande d’annulation : l’appelant attend que toutes les tâches se terminent.However, tasks aren't abandoned after cancellation is requested—the caller awaits all tasks to complete.
Si l’application s’arrête inopinément (par exemple en cas d’échec du processus de l’application),
StopAsync
n’est probablement pas appelée.If the app shuts down unexpectedly (for example, the app's process fails),StopAsync
might not be called. Par conséquent, les méthodes appelées ou les opérations effectuées dansStopAsync
peuvent ne pas se produire.Therefore, any methods called or operations conducted inStopAsync
might not occur.Pour prolonger le délai d’expiration par défaut de cinq secondes, définissez :To extend the default five second shutdown timeout, set:
- ShutdownTimeout quand vous utilisez l’hôte générique.ShutdownTimeout when using Generic Host. Pour plus d’informations, consultez Hôte générique .NET dans ASP.NET Core.For more information, see Hôte générique .NET dans ASP.NET Core.
- Le paramètre de configuration du délai d’expiration de l’hôte quand vous utilisez l’hôte web.Shutdown timeout host configuration setting when using Web Host. Pour plus d’informations, consultez Hôte web ASP.NET Core.For more information, see Hôte web ASP.NET Core.
Le service hébergé est activé une seule fois au démarrage de l’application et s’arrête normalement à l’arrêt de l’application.The hosted service is activated once at app startup and gracefully shut down at app shutdown. Si une erreur est levée pendant l’exécution des tâches d’arrière-plan, Dispose
doit être appelée même si StopAsync
n’est pas appelée.If an error is thrown during background task execution, Dispose
should be called even if StopAsync
isn't called.
Tâche d’arrière-plan avec minuteurTimed background tasks
Une tâche d’arrière-plan avec minuteur utilise la classe System.Threading.Timer.A timed background task makes use of the System.Threading.Timer class. Le minuteur déclenche la méthode DoWork
de la tâche.The timer triggers the task's DoWork
method. Le minuteur est désactivé sur StopAsync
et supprimé quand le conteneur du service est supprimé sur 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();
}
}
Le Timer n’attend pas que les exécutions précédentes se DoWork
terminent. l’approche illustrée peut donc ne pas convenir à chaque scénario.The Timer doesn't wait for previous executions of DoWork
to finish, so the approach shown might not be suitable for every scenario.
Le service est inscrit dans Startup.ConfigureServices
avec la méthode d’extension AddHostedService
:The service is registered in Startup.ConfigureServices
with the AddHostedService
extension method:
services.AddHostedService<TimedHostedService>();
Utilisation d’un service délimité dans une tâche d’arrière-planConsuming a scoped service in a background task
Pour utiliser les services délimités dans un IHostedService
objet, créez une étendue.To use scoped services within an IHostedService
, create a scope. Par défaut, aucune étendue n’est créée pour un service hébergé.No scope is created for a hosted service by default.
Le service des tâches d’arrière-plan délimitées contient la logique de la tâche d’arrière-plan.The scoped background task service contains the background task's logic. Dans l’exemple suivant, un ILogger est injecté dans le service :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.");
}
}
Le service hébergé crée une étendue pour résoudre le service des tâches d’arrière-plan délimitées pour appeler sa méthode 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;
}
}
Les services sont inscrits dans Startup.ConfigureServices
.The services are registered in Startup.ConfigureServices
. L’implémentation IHostedService
est inscrite avec la méthode d’extension AddHostedService
:The IHostedService
implementation is registered with the AddHostedService
extension method:
services.AddHostedService<ConsumeScopedServiceHostedService>();
services.AddScoped<IScopedProcessingService, ScopedProcessingService>();
Tâches d’arrière-plan en file d’attenteQueued background tasks
Une file d’attente de tâches en arrière-plan est basée sur le .NET Framework 4. x QueueBackgroundWorkItem (prévu provisoirement pour être intégré pour 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;
}
}
Dans QueueHostedService
, les tâches d'arrière-plan dans la file d'attente sont retirées de la file d'attente et exécutées en tant que BackgroundService, qui est une classe de base pour l’implémentation d’une exécution longue 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.");
}
}
Les services sont inscrits dans Startup.ConfigureServices
.The services are registered in Startup.ConfigureServices
. L’implémentation IHostedService
est inscrite avec la méthode d’extension AddHostedService
:The IHostedService
implementation is registered with the AddHostedService
extension method:
services.AddHostedService<QueuedHostedService>();
services.AddSingleton<IBackgroundTaskQueue, BackgroundTaskQueue>();
Dans la classe de modèle de page Index :In the Index page model class:
IBackgroundTaskQueue
est injecté dans le constructeur et affecté àQueue
.TheIBackgroundTaskQueue
is injected into the constructor and assigned toQueue
.- Un IServiceScopeFactory est injecté et affecté à
_serviceScopeFactory
.An IServiceScopeFactory is injected and assigned to_serviceScopeFactory
. La fabrique est utilisée pour créer des instances de IServiceScope, qui servent à créer des services au sein d’une étendue.The factory is used to create instances of IServiceScope, which is used to create services within a scope. Une étendue est créée de façon à utiliser l’élémentAppDbContext
(service délimité) de l’application pour écrire des enregistrements de base de données dansIBackgroundTaskQueue
(service singleton).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; }
Quand le bouton Ajouter une tâche est sélectionné dans la page Index, la méthode OnPostAddTask
est exécutée.When the Add Task button is selected on the Index page, the OnPostAddTask
method is executed. QueueBackgroundWorkItem
est appelé pour empiler un élément de travail :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();
}
Ressources supplémentairesAdditional resources
- Implémenter des tâches d’arrière-plan dans des microservices avec IHostedService et la classe BackgroundServiceImplement background tasks in microservices with IHostedService and the BackgroundService class
- Exécuter des tâches en arrière-plan avec WebJobs dans Azure App ServiceRun background tasks with WebJobs in Azure App Service
- Timer