J'essaie d'exécuter une tâche asynchrone lors du chargement d'une page dans ASP.Net Core, c'est-à-dire que je veux que la tâche soit exécutée dès que l'utilisateur l'achemine vers la page, mais que la page soit affichée avant la fin de la tâche. Il semble qu'avec ASP.Net Core, vous utilisez un middleware pour effectuer de telles tâches. J'ai donc essayé d'ajouter ce qui suit à Startup.cs
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IServiceProvider serviceProvider)
{
// Other configurations here
app.Use(async (context, next) =>
{
if (context.Request.Path.Value.Contains("PageWithAsyncTask"))
{
var serviceWithAsyncTask = serviceProvider.GetService<IMyService>();
await serviceWithAsyncTask .DoAsync();
}
await next.Invoke();
});
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
Le problème avec ce qui précède est qu’il ya un retard dans le chargement de la page jusqu’à ce que DoAsync
soit terminé puisque nous n’appelons pas next.Invoke()
tant que DoAsync
n’est pas terminé. Comment est-ce que j'implémente correctement ce qui précède de telle sorte que next.Invoke()
soit appelé immédiatement après que DoAsync
soit en cours d'exécution?
ASP.NET n'a pas été conçu pour les tâches en arrière-plan. I fortement recommande d'utiliser une architecture appropriée, telle que Fonctions Azure/WebJobs/Worker Roles/Services Win32/etc, avec une file d'attente fiable (files d'attente Azure/MSMQ/etc) avec laquelle l'application ASP.NET peut parler. son service.
Toutefois, si vous vraiment voulez - et êtes prêt à accepter les risques (en particulier, que votre travail puisse être interrompu), vous pouvez utiliser IApplicationLifetime
.
Dans ASP.NET Core 2, IHostedService est conçu pour exécuter vos tâches en arrière-plan. Enregistrez le service IHostedService en tant que Singleton et il sera automatiquement lancé au démarrage:
Depuis Asp.Net core 2.1 , pour utiliser des tâches en arrière-plan, il est très pratique d'implémenter IHostedService
en dérivant de la classe BackgroundService
base. Voici l'échantillon pris de ici :
public class MyServiceA : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
Console.WriteLine("MyServiceA is starting.");
stoppingToken.Register(() => Console.WriteLine("MyServiceA is stopping."));
while (!stoppingToken.IsCancellationRequested)
{
Console.WriteLine("MyServiceA is doing background work.");
await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken);
}
Console.WriteLine("MyServiceA background task is stopping.");
}
}
Ensuite, enregistrez-le simplement dans Startup.ConfigureServices
:
services.AddSingleton<IHostedService, MyServiceA>();
Et, comme l'a noté Stephen Cleary, l'application Asp.Net
n'est peut-être pas le meilleur endroit pour les tâches en arrière-plan (par exemple, lorsque l'application est hébergée dans IIS, elle peut être fermée en raison du recyclage du pool d'applications), mais elle peut être appliquée. très bien.
Au lieu de
await serviceWithAsyncTask .DoAsync();
vous pourriez utiliser
ThreadPool.QueueUserWorkItem(delegate {
SomeMethod();
});
Dans cette approche, un thread supplémentaire sera utilisé à partir du pool de threads, ce qui est bien sûr indispensable si vous souhaitez que le code s'exécute sur un thread autre que le thread principal :-)
Tout code placé après ce bloc sera exécuté immédiatement. Notez également que si votre processus de serveur Web (kestral) est recyclé par IIS ou par le proxy inverse que vous utilisez, votre agent d'arrière-plan sera immédiatement abandonné. Votre travailleur d'arrière-plan doit donc être écrit de manière défensive, dans cet esprit.
Veuillez également noter que SomeMethod()
n'est pas en soi une méthode async
. Mais il est appelé à partir d’un thread d’arrière-plan, de sorte qu’il tourne de manière asynchrone (c’est-à-dire indépendamment du thread principal).
Regardez HangFire pour la gestion du traitement en arrière-plan, fonctionne très bien dans .Net Core: https://www.hangfire.io/