Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Av Jeow Li Huan
Varning
Den här versionen av ASP.NET Core stöds inte längre. Mer information finns i supportpolicyn för .NET och .NET Core. För den nuvarande utgåvan, se .NET 9-versionen av den här artikeln .
Viktigt!
Den här informationen gäller en förhandsversionsprodukt som kan ändras avsevärt innan den släpps kommersiellt. Microsoft lämnar inga garantier, uttryckliga eller underförstådda, med avseende på den information som tillhandahålls här.
För den nuvarande utgåvan, se .NET 9-versionen av den här artikeln .
I ASP.NET Core kan bakgrundsaktiviteter implementeras som värdbaserade tjänster. En värdbaserad tjänst är en klass med bakgrundsaktivitetslogik som implementerar IHostedService gränssnittet. Den här artikeln innehåller tre exempel på värdbaserade tjänster:
- Bakgrundsaktivitet som körs på en tidtagare.
- Värdbaserad tjänst som aktiverar en begränsad tjänst. Den begränsade tjänsten kan använda beroendeinmatning (DI).
- Köade bakgrundsaktiviteter som körs sekventiellt.
Arbetarstjänstmall
Mallen ASP.NET Core Worker Service är en startpunkt för att skriva tidskrävande tjänstappar. En app som skapats från Worker Service-mallen anger Worker SDK i sin projektfil:
<Project Sdk="Microsoft.NET.Sdk.Worker">
Så här använder du mallen som grund för en värdbaserad tjänstapp:
- Skapa ett nytt projekt.
- Välj Worker Service. Välj Nästa.
- Ange ett projektnamn i fältet Projektnamn eller godkänn standardprojektets namn. Välj Nästa.
- I dialogrutan Ytterligare information väljer du ett ramverk. Välj Skapa.
Paket
En app som baseras på Worker Service-mallen Microsoft.NET.Sdk.Worker
använder SDK:t och har en explicit paketreferens till Microsoft.Extensions.Hosting-paketet . Se till exempel exempelappens projektfil (BackgroundTasksSample.csproj
).
För webbappar som använder Microsoft.NET.Sdk.Web
SDK refereras Microsoft.Extensions.Hosting-paketet implicit från det delade ramverket. En explicit paketreferens i appens projektfil krävs inte.
IHostedService-gränssnitt
Gränssnittet IHostedService definierar två metoder för objekt som hanteras av värden:
StartAsync
StartAsync(CancellationToken) innehåller logiken för att starta bakgrundsaktiviteten.
StartAsync
kallas innan:
- Appens pipeline för bearbetning av begäranden har konfigurerats.
- Servern startas och IApplicationLifetime.ApplicationStarted utlöses.
StartAsync
bör begränsas till kortvariga uppgifter eftersom värdbaserade tjänster körs sekventiellt och inga ytterligare tjänster startas förrän körningarna har slutförts StartAsync
.
StopAsync
-
StopAsync (CancellationToken) utlöses när värden utför en graciös avstängning.
StopAsync
innehåller logiken för att avsluta bakgrundsaktiviteten. Implementera IDisposable och slutförare (destruatorer) för att ta bort ohanterade resurser.
Annulleringstoken har en standardtidsgräns på 30 sekunder för att indikera att avstängningsprocessen inte längre ska vara mjuk. När annullering begärs för token:
- Eventuella återstående bakgrundsåtgärder som appen utför bör avbrytas.
- Alla metoder som anropas
StopAsync
bör returneras omedelbart.
Aktiviteter avbryts dock inte när annulleringen har begärts – anroparen väntar på att alla aktiviteter ska slutföras.
Om appen stängs av oväntat (till exempel misslyckas appens process) StopAsync
kanske den inte anropas. Därför kanske inte några metoder som anropas eller åtgärder som utförs i StopAsync
inträffar.
Om du vill utöka standardgränsen på 30 sekunder för avstängning anger du:
- ShutdownTimeout när du använder allmän värd. Mer information finns i .NET Generic Host i ASP.NET Core.
- Inställning för avstängningstid vid värdkonfiguration när en webbvärd används. Mer information finns i ASP.NET Core Web Host.
Den värdbaserade tjänsten aktiveras en gång vid appstart och stängs av på ett smidigt sätt vid appavstängning. Om ett fel utlöses under körning av bakgrundsaktiviteter ska Dispose
anropas, även om StopAsync
inte anropas.
BackgroundService-basklass
BackgroundService är en basklass för att implementera en långvarig IHostedService.
ExecuteAsync(CancellationToken) anropas för att köra bakgrundstjänsten. Implementeringen returnerar en Task som representerar bakgrundstjänstens hela livslängd. Inga ytterligare tjänster startas förrän ExecuteAsync blir asynkront, till exempel genom att anropa await
. Undvik att utföra långa, blockerande initialiseringsarbete i ExecuteAsync
. Värden blockerar StopAsync(CancellationToken) medan den väntar på att ExecuteAsync
ska slutföras.
Annulleringstoken utlöses när IHostedService.StopAsync anropas. Implementeringen av ExecuteAsync
bör slutföras omedelbart när annulleringstoken utlöses för att kunna stänga av tjänsten på ett korrekt sätt. Annars stängs tjänsten okontrollerat av vid avstängningens tidsgräns. Mer information finns i avsnittet IHostedService-gränssnitt .
Mer information finns i BackgroundService-källkoden .
Tidsåtgång för bakgrundsaktiviteter
En tidsinställd bakgrundsaktivitet använder klassen System.Threading.Timer . Timern utlöser aktivitetens DoWork
metod. Timern inaktiveras på StopAsync
och tas bort när tjänstcontainern tas bort på Dispose
:
public class TimedHostedService : IHostedService, IDisposable
{
private int executionCount = 0;
private readonly ILogger<TimedHostedService> _logger;
private Timer? _timer = null;
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();
}
}
Timer väntar inte på att tidigare körningar av DoWork
ska slutföras, därför kanske den visade metoden inte är lämplig för varje scenario.
Interlocked.Increment används för att öka körningsräknaren som en atomisk åtgärd, vilket säkerställer att flera trådar inte uppdateras executionCount
samtidigt.
Tjänsten är registrerad i IHostBuilder.ConfigureServices
(Program.cs
) med AddHostedService
tilläggsmetoden:
services.AddHostedService<TimedHostedService>();
Använda en avgränsad service i en bakgrundsuppgift
Om du vill använda begränsade tjänster inom en BackgroundService skapar du ett omfång. Inget omfång skapas för en värdbaserad tjänst som standard.
Tjänsten för begränsade bakgrundsaktiviteter innehåller bakgrundsaktivitetens logik. I följande exempel:
- Tjänsten är asynkron. Metoden
DoWork
returnerar enTask
. I demonstrationssyfte väntar en fördröjning på tio sekunder iDoWork
metoden. - En ILogger matas in i tjänsten.
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);
}
}
}
Den värdbaserade tjänsten skapar ett omfång för att lösa den begränsade bakgrundsaktivitetstjänsten så att den kan anropa sin DoWork
metod.
DoWork
returnerar en Task
, som väntas i 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);
}
}
Tjänsterna är registrerade i IHostBuilder.ConfigureServices
(Program.cs
). Den värdbaserade tjänsten är registrerad med AddHostedService
tilläggsmetoden:
services.AddHostedService<ConsumeScopedServiceHostedService>();
services.AddScoped<IScopedProcessingService, ScopedProcessingService>();
Bakgrundsaktiviteter i kö
En bakgrundsaktivitetskö baseras på .NET Framework 4.x QueueBackgroundWorkItem:
public interface IBackgroundTaskQueue
{
ValueTask QueueBackgroundWorkItemAsync(Func<CancellationToken, ValueTask> workItem);
ValueTask<Func<CancellationToken, ValueTask>> DequeueAsync(
CancellationToken cancellationToken);
}
public class BackgroundTaskQueue : IBackgroundTaskQueue
{
private readonly Channel<Func<CancellationToken, ValueTask>> _queue;
public BackgroundTaskQueue(int capacity)
{
// Capacity should be set based on the expected application load and
// number of concurrent threads accessing the queue.
// BoundedChannelFullMode.Wait will cause calls to WriteAsync() to return a task,
// which completes only when space became available. This leads to backpressure,
// in case too many publishers/calls start accumulating.
var options = new BoundedChannelOptions(capacity)
{
FullMode = BoundedChannelFullMode.Wait
};
_queue = Channel.CreateBounded<Func<CancellationToken, ValueTask>>(options);
}
public async ValueTask QueueBackgroundWorkItemAsync(
Func<CancellationToken, ValueTask> workItem)
{
if (workItem == null)
{
throw new ArgumentNullException(nameof(workItem));
}
await _queue.Writer.WriteAsync(workItem);
}
public async ValueTask<Func<CancellationToken, ValueTask>> DequeueAsync(
CancellationToken cancellationToken)
{
var workItem = await _queue.Reader.ReadAsync(cancellationToken);
return workItem;
}
}
I följande QueueHostedService
exempel:
- Metoden
BackgroundProcessing
returnerar enTask
, som väntar iExecuteAsync
. - Bakgrundsuppgifter i kön tas bort och körs i
BackgroundProcessing
. - Arbetsobjekt väntar innan tjänsten stoppas i
StopAsync
.
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);
}
}
En MonitorLoop
tjänst hanterar lagringsuppgifter för den värdbaserade tjänsten när w
nyckeln väljs på en indataenhet:
-
IBackgroundTaskQueue
injiceras iMonitorLoop
-tjänsten. -
IBackgroundTaskQueue.QueueBackgroundWorkItem
anropas för att lägga till ett arbetsobjekt i kön. - Arbetsobjektet simulerar en långvarig bakgrundsaktivitet:
- Tre fördröjningar utförs på 5 sekunder vardera (
Task.Delay
). - En
try-catch
sats fångar OperationCanceledException om aktiviteten avbryts.
- Tre fördröjningar utförs på 5 sekunder vardera (
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("MonitorAsync Loop is starting.");
// Run a console user input loop in a background thread
Task.Run(async () => await MonitorAsync());
}
private async ValueTask MonitorAsync()
{
while (!_cancellationToken.IsCancellationRequested)
{
var keyStroke = Console.ReadKey();
if (keyStroke.Key == ConsoleKey.W)
{
// Enqueue a background work item
await _taskQueue.QueueBackgroundWorkItemAsync(BuildWorkItem);
}
}
}
private async ValueTask BuildWorkItem(CancellationToken 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);
}
}
}
Tjänsterna är registrerade i IHostBuilder.ConfigureServices
(Program.cs
). Den värdbaserade tjänsten är registrerad med AddHostedService
tilläggsmetoden:
services.AddSingleton<MonitorLoop>();
services.AddHostedService<QueuedHostedService>();
services.AddSingleton<IBackgroundTaskQueue>(ctx =>
{
if (!int.TryParse(hostContext.Configuration["QueueCapacity"], out var queueCapacity))
queueCapacity = 100;
return new BackgroundTaskQueue(queueCapacity);
});
MonitorLoop
startas i Program.cs
:
var monitorLoop = host.Services.GetRequiredService<MonitorLoop>();
monitorLoop.StartMonitorLoop();
Asynkron tidsinställd bakgrundsaktivitet
Följande kod skapar en asynkron tidsinställd bakgrundsaktivitet:
namespace TimedBackgroundTasks;
public class TimedHostedService : BackgroundService
{
private readonly ILogger<TimedHostedService> _logger;
private int _executionCount;
public TimedHostedService(ILogger<TimedHostedService> logger)
{
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("Timed Hosted Service running.");
// When the timer should have no due-time, then do the work once now.
await DoWork();
using PeriodicTimer timer = new(TimeSpan.FromSeconds(30));
try
{
while (await timer.WaitForNextTickAsync(stoppingToken))
{
await DoWork();
}
}
catch (OperationCanceledException)
{
_logger.LogInformation("Timed Hosted Service is stopping.");
}
}
private async Task DoWork()
{
int count = Interlocked.Increment(ref _executionCount);
// Simulate work
await Task.Delay(TimeSpan.FromSeconds(2));
_logger.LogInformation("Timed Hosted Service is working. Count: {Count}", count);
}
}
Inbyggd AOT
Worker Service-mallarna har stöd för .NET native ahead-of-time (AOT) med --aot
flaggan:
- Skapa ett nytt projekt.
- Välj Worker Service. Välj Nästa.
- Ange ett projektnamn i fältet Projektnamn eller godkänn standardprojektets namn. Välj Nästa.
- I dialogrutan Ytterligare information :
- Välj ett ramverk.
- Markera kryssrutan Aktivera intern AOT-publicering .
- Välj Skapa.
AOT-alternativet lägger <PublishAot>true</PublishAot>
till i projektfilen:
<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>
Ytterligare resurser
I ASP.NET Core kan bakgrundsaktiviteter implementeras som värdbaserade tjänster. En värdbaserad tjänst är en klass med bakgrundsaktivitetslogik som implementerar IHostedService gränssnittet. Den här artikeln innehåller tre exempel på värdbaserade tjänster:
- Bakgrundsaktivitet som körs på en tidtagare.
- Värdbaserad tjänst som aktiverar en begränsad tjänst. Den begränsade tjänsten kan använda beroendeinmatning (DI).
- Köade bakgrundsaktiviteter som körs sekventiellt.
Arbetarstjänstmall
Mallen ASP.NET Core Worker Service är en startpunkt för att skriva tidskrävande tjänstappar. En app som skapats från Worker Service-mallen anger Worker SDK i sin projektfil:
<Project Sdk="Microsoft.NET.Sdk.Worker">
Så här använder du mallen som grund för en värdbaserad tjänstapp:
- Skapa ett nytt projekt.
- Välj Worker Service. Välj Nästa.
- Ange ett projektnamn i fältet Projektnamn eller godkänn standardprojektets namn. Välj Nästa.
- I dialogrutan Ytterligare information väljer du ett ramverk. Välj Skapa.
Paket
En app som baseras på Worker Service-mallen Microsoft.NET.Sdk.Worker
använder SDK:t och har en explicit paketreferens till Microsoft.Extensions.Hosting-paketet . Se till exempel exempelappens projektfil (BackgroundTasksSample.csproj
).
För webbappar som använder Microsoft.NET.Sdk.Web
SDK refereras Microsoft.Extensions.Hosting-paketet implicit från det delade ramverket. En explicit paketreferens i appens projektfil krävs inte.
IHostedService-gränssnitt
Gränssnittet IHostedService definierar två metoder för objekt som hanteras av värden:
StartAsync
StartAsync(CancellationToken) innehåller logiken för att starta bakgrundsaktiviteten.
StartAsync
kallas innan:
- Appens pipeline för bearbetning av begäranden har konfigurerats.
- Servern startas och IApplicationLifetime.ApplicationStarted utlöses.
StartAsync
bör begränsas till kortvariga uppgifter eftersom värdbaserade tjänster körs sekventiellt och inga ytterligare tjänster startas förrän körningarna har slutförts StartAsync
.
StopAsync
-
StopAsync (CancellationToken) utlöses när värden utför en graciös avstängning.
StopAsync
innehåller logiken för att avsluta bakgrundsaktiviteten. Implementera IDisposable och slutförare (destruatorer) för att ta bort ohanterade resurser.
Annulleringstoken har en standardtidsgräns på 30 sekunder för att indikera att avstängningsprocessen inte längre ska vara mjuk. När annullering begärs för token:
- Eventuella återstående bakgrundsåtgärder som appen utför bör avbrytas.
- Alla metoder som anropas
StopAsync
bör returneras omedelbart.
Aktiviteter avbryts dock inte när annulleringen har begärts – anroparen väntar på att alla aktiviteter ska slutföras.
Om appen stängs av oväntat (till exempel misslyckas appens process) StopAsync
kanske den inte anropas. Därför kanske inte några metoder som anropas eller åtgärder som utförs i StopAsync
inträffar.
Om du vill utöka standardgränsen på 30 sekunder för avstängning anger du:
- ShutdownTimeout när du använder allmän värd. Mer information finns i .NET Generic Host i ASP.NET Core.
- Inställning för avstängningstid vid värdkonfiguration när en webbvärd används. Mer information finns i ASP.NET Core Web Host.
Den värdbaserade tjänsten aktiveras en gång vid appstart och stängs av på ett smidigt sätt vid appavstängning. Om ett fel utlöses under körning av bakgrundsaktiviteter ska Dispose
anropas, även om StopAsync
inte anropas.
BackgroundService-basklass
BackgroundService är en basklass för att implementera en långvarig IHostedService.
ExecuteAsync(CancellationToken) anropas för att köra bakgrundstjänsten. Implementeringen returnerar en Task som representerar bakgrundstjänstens hela livslängd. Inga ytterligare tjänster startas förrän ExecuteAsync blir asynkront, till exempel genom att anropa await
. Undvik att utföra långa, blockerande initialiseringsarbete i ExecuteAsync
. Värden blockerar StopAsync(CancellationToken) medan den väntar på att ExecuteAsync
ska slutföras.
Annulleringstoken utlöses när IHostedService.StopAsync anropas. Implementeringen av ExecuteAsync
bör slutföras omedelbart när annulleringstoken utlöses för att kunna stänga av tjänsten på ett korrekt sätt. Annars stängs tjänsten okontrollerat av vid avstängningens tidsgräns. Mer information finns i avsnittet IHostedService-gränssnitt .
Mer information finns i BackgroundService-källkoden .
Tidsåtgång för bakgrundsaktiviteter
En tidsinställd bakgrundsaktivitet använder klassen System.Threading.Timer . Timern utlöser aktivitetens DoWork
metod. Timern inaktiveras på StopAsync
och tas bort när tjänstcontainern tas bort på Dispose
:
public class TimedHostedService : IHostedService, IDisposable
{
private int executionCount = 0;
private readonly ILogger<TimedHostedService> _logger;
private Timer? _timer = null;
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();
}
}
Timer väntar inte på att tidigare körningar av DoWork
ska slutföras, därför kanske den visade metoden inte är lämplig för varje scenario.
Interlocked.Increment används för att öka körningsräknaren som en atomisk åtgärd, vilket säkerställer att flera trådar inte uppdateras executionCount
samtidigt.
Tjänsten är registrerad i IHostBuilder.ConfigureServices
(Program.cs
) med AddHostedService
tilläggsmetoden:
services.AddHostedService<TimedHostedService>();
Använda en avgränsad service i en bakgrundsuppgift
Om du vill använda begränsade tjänster inom en BackgroundService skapar du ett omfång. Inget omfång skapas för en värdbaserad tjänst som standard.
Tjänsten för begränsade bakgrundsaktiviteter innehåller bakgrundsaktivitetens logik. I följande exempel:
- Tjänsten är asynkron. Metoden
DoWork
returnerar enTask
. I demonstrationssyfte väntar en fördröjning på tio sekunder iDoWork
metoden. - En ILogger matas in i tjänsten.
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);
}
}
}
Den värdbaserade tjänsten skapar ett omfång för att lösa den begränsade bakgrundsaktivitetstjänsten så att den kan anropa sin DoWork
metod.
DoWork
returnerar en Task
, som väntas i 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);
}
}
Tjänsterna är registrerade i IHostBuilder.ConfigureServices
(Program.cs
). Den värdbaserade tjänsten är registrerad med AddHostedService
tilläggsmetoden:
services.AddHostedService<ConsumeScopedServiceHostedService>();
services.AddScoped<IScopedProcessingService, ScopedProcessingService>();
Bakgrundsaktiviteter i kö
En bakgrundsaktivitetskö baseras på .NET Framework 4.x QueueBackgroundWorkItem:
public interface IBackgroundTaskQueue
{
ValueTask QueueBackgroundWorkItemAsync(Func<CancellationToken, ValueTask> workItem);
ValueTask<Func<CancellationToken, ValueTask>> DequeueAsync(
CancellationToken cancellationToken);
}
public class BackgroundTaskQueue : IBackgroundTaskQueue
{
private readonly Channel<Func<CancellationToken, ValueTask>> _queue;
public BackgroundTaskQueue(int capacity)
{
// Capacity should be set based on the expected application load and
// number of concurrent threads accessing the queue.
// BoundedChannelFullMode.Wait will cause calls to WriteAsync() to return a task,
// which completes only when space became available. This leads to backpressure,
// in case too many publishers/calls start accumulating.
var options = new BoundedChannelOptions(capacity)
{
FullMode = BoundedChannelFullMode.Wait
};
_queue = Channel.CreateBounded<Func<CancellationToken, ValueTask>>(options);
}
public async ValueTask QueueBackgroundWorkItemAsync(
Func<CancellationToken, ValueTask> workItem)
{
if (workItem == null)
{
throw new ArgumentNullException(nameof(workItem));
}
await _queue.Writer.WriteAsync(workItem);
}
public async ValueTask<Func<CancellationToken, ValueTask>> DequeueAsync(
CancellationToken cancellationToken)
{
var workItem = await _queue.Reader.ReadAsync(cancellationToken);
return workItem;
}
}
I följande QueueHostedService
exempel:
- Metoden
BackgroundProcessing
returnerar enTask
, som väntar iExecuteAsync
. - Bakgrundsuppgifter i kön tas bort och körs i
BackgroundProcessing
. - Arbetsobjekt väntar innan tjänsten stoppas i
StopAsync
.
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);
}
}
En MonitorLoop
tjänst hanterar lagringsuppgifter för den värdbaserade tjänsten när w
nyckeln väljs på en indataenhet:
-
IBackgroundTaskQueue
injiceras iMonitorLoop
-tjänsten. -
IBackgroundTaskQueue.QueueBackgroundWorkItem
anropas för att lägga till ett arbetsobjekt i kön. - Arbetsobjektet simulerar en långvarig bakgrundsaktivitet:
- Tre fördröjningar utförs på 5 sekunder vardera (
Task.Delay
). - En
try-catch
sats fångar OperationCanceledException om aktiviteten avbryts.
- Tre fördröjningar utförs på 5 sekunder vardera (
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("MonitorAsync Loop is starting.");
// Run a console user input loop in a background thread
Task.Run(async () => await MonitorAsync());
}
private async ValueTask MonitorAsync()
{
while (!_cancellationToken.IsCancellationRequested)
{
var keyStroke = Console.ReadKey();
if (keyStroke.Key == ConsoleKey.W)
{
// Enqueue a background work item
await _taskQueue.QueueBackgroundWorkItemAsync(BuildWorkItem);
}
}
}
private async ValueTask BuildWorkItem(CancellationToken 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);
}
}
}
Tjänsterna är registrerade i IHostBuilder.ConfigureServices
(Program.cs
). Den värdbaserade tjänsten är registrerad med AddHostedService
tilläggsmetoden:
services.AddSingleton<MonitorLoop>();
services.AddHostedService<QueuedHostedService>();
services.AddSingleton<IBackgroundTaskQueue>(ctx =>
{
if (!int.TryParse(hostContext.Configuration["QueueCapacity"], out var queueCapacity))
queueCapacity = 100;
return new BackgroundTaskQueue(queueCapacity);
});
MonitorLoop
startas i Program.cs
:
var monitorLoop = host.Services.GetRequiredService<MonitorLoop>();
monitorLoop.StartMonitorLoop();
Asynkron tidsinställd bakgrundsaktivitet
Följande kod skapar en asynkron tidsinställd bakgrundsaktivitet:
namespace TimedBackgroundTasks;
public class TimedHostedService : BackgroundService
{
private readonly ILogger<TimedHostedService> _logger;
private int _executionCount;
public TimedHostedService(ILogger<TimedHostedService> logger)
{
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("Timed Hosted Service running.");
// When the timer should have no due-time, then do the work once now.
await DoWork();
using PeriodicTimer timer = new(TimeSpan.FromSeconds(30));
try
{
while (await timer.WaitForNextTickAsync(stoppingToken))
{
await DoWork();
}
}
catch (OperationCanceledException)
{
_logger.LogInformation("Timed Hosted Service is stopping.");
}
}
private async Task DoWork()
{
int count = Interlocked.Increment(ref _executionCount);
// Simulate work
await Task.Delay(TimeSpan.FromSeconds(2));
_logger.LogInformation("Timed Hosted Service is working. Count: {Count}", count);
}
}
Ytterligare resurser
I ASP.NET Core kan bakgrundsaktiviteter implementeras som värdbaserade tjänster. En värdbaserad tjänst är en klass med bakgrundsaktivitetslogik som implementerar IHostedService gränssnittet. Den här artikeln innehåller tre exempel på värdbaserade tjänster:
- Bakgrundsaktivitet som körs på en tidtagare.
- Värdbaserad tjänst som aktiverar en begränsad tjänst. Den begränsade tjänsten kan använda beroendeinmatning (DI).
- Köade bakgrundsaktiviteter som körs sekventiellt.
Visa eller ladda ned exempelkod (hur du laddar ned)
Arbetarstjänstmall
Mallen ASP.NET Core Worker Service är en startpunkt för att skriva tidskrävande tjänstappar. En app som skapats från Worker Service-mallen anger Worker SDK i sin projektfil:
<Project Sdk="Microsoft.NET.Sdk.Worker">
Så här använder du mallen som grund för en värdbaserad tjänstapp:
- Skapa ett nytt projekt.
- Välj Worker Service. Välj Nästa.
- Ange ett projektnamn i fältet Projektnamn eller godkänn standardprojektets namn. Välj Skapa.
- I dialogrutan Skapa en ny Arbetstjänst väljer du Skapa.
Paket
En app som baseras på Worker Service-mallen Microsoft.NET.Sdk.Worker
använder SDK:t och har en explicit paketreferens till Microsoft.Extensions.Hosting-paketet . Se till exempel exempelappens projektfil (BackgroundTasksSample.csproj
).
För webbappar som använder Microsoft.NET.Sdk.Web
SDK refereras Microsoft.Extensions.Hosting-paketet implicit från det delade ramverket. En explicit paketreferens i appens projektfil krävs inte.
IHostedService-gränssnitt
Gränssnittet IHostedService definierar två metoder för objekt som hanteras av värden:
StartAsync
StartAsync
innehåller logiken för att starta bakgrundsaktiviteten.
StartAsync
kallas innan:
- Appens pipeline för bearbetning av begäranden har konfigurerats.
- Servern startas och IApplicationLifetime.ApplicationStarted utlöses.
Standardbeteendet kan ändras så att den värdbaserade tjänsten körs StartAsync
efter att appens pipeline har konfigurerats och ApplicationStarted
anropas. Om du vill ändra standardbeteendet lägger du till den värdbaserade tjänsten (VideosWatcher
i följande exempel) efter att du har anropat ConfigureWebHostDefaults
:
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
-
StopAsync (CancellationToken) utlöses när värden utför en graciös avstängning.
StopAsync
innehåller logiken för att avsluta bakgrundsaktiviteten. Implementera IDisposable och slutförare (destruatorer) för att ta bort ohanterade resurser.
Annulleringstokenet har en fem sekunders standardtimeout som anger att avstängningsprocessen inte längre ska vara smidig. När annullering begärs för token:
- Eventuella återstående bakgrundsåtgärder som appen utför bör avbrytas.
- Alla metoder som anropas
StopAsync
bör returneras omedelbart.
Aktiviteter avbryts dock inte när annulleringen har begärts – anroparen väntar på att alla aktiviteter ska slutföras.
Om appen stängs av oväntat (till exempel misslyckas appens process) StopAsync
kanske den inte anropas. Därför kanske inte några metoder som anropas eller åtgärder som utförs i StopAsync
inträffar.
Om du vill utöka tidsgränsen för fem sekunders standardavstängning anger du:
- ShutdownTimeout när du använder allmän värd. Mer information finns i .NET Generic Host i ASP.NET Core.
- Inställning för avstängningstid vid värdkonfiguration när en webbvärd används. Mer information finns i ASP.NET Core Web Host.
Den värdbaserade tjänsten aktiveras en gång vid appstart och stängs av på ett smidigt sätt vid appavstängning. Om ett fel utlöses under körning av bakgrundsaktiviteter ska Dispose
anropas, även om StopAsync
inte anropas.
BackgroundService-basklass
BackgroundService är en basklass för att implementera en långvarig IHostedService.
ExecuteAsync(CancellationToken) anropas för att köra bakgrundstjänsten. Implementeringen returnerar en Task som representerar bakgrundstjänstens hela livslängd. Inga ytterligare tjänster startas förrän ExecuteAsync blir asynkront, till exempel genom att anropa await
. Undvik att utföra långa, blockerande initialiseringsarbete i ExecuteAsync
. Värden blockerar StopAsync(CancellationToken) medan den väntar på att ExecuteAsync
ska slutföras.
Annulleringstoken utlöses när IHostedService.StopAsync anropas. Implementeringen av ExecuteAsync
bör slutföras omedelbart när annulleringstoken utlöses för att kunna stänga av tjänsten på ett korrekt sätt. Annars stängs tjänsten okontrollerat av vid avstängningens tidsgräns. Mer information finns i avsnittet IHostedService-gränssnitt .
StartAsync
bör begränsas till kortvariga uppgifter eftersom värdbaserade tjänster körs sekventiellt och inga ytterligare tjänster startas förrän körningarna har slutförts StartAsync
. Tidskrävande uppgifter ska placeras i ExecuteAsync
. Mer information finns i källan till BackgroundService.
Tidsåtgång för bakgrundsaktiviteter
En tidsinställd bakgrundsaktivitet använder klassen System.Threading.Timer . Timern utlöser aktivitetens DoWork
metod. Timern inaktiveras på StopAsync
och tas bort när tjänstcontainern tas bort på 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();
}
}
Timer väntar inte på att tidigare körningar av DoWork
ska slutföras, därför kanske den visade metoden inte är lämplig för varje scenario.
Interlocked.Increment används för att öka körningsräknaren som en atomisk åtgärd, vilket säkerställer att flera trådar inte uppdateras executionCount
samtidigt.
Tjänsten är registrerad i IHostBuilder.ConfigureServices
(Program.cs
) med AddHostedService
tilläggsmetoden:
services.AddHostedService<TimedHostedService>();
Använda en avgränsad service i en bakgrundsuppgift
Om du vill använda begränsade tjänster inom en BackgroundService skapar du ett omfång. Inget omfång skapas för en värdbaserad tjänst som standard.
Tjänsten för begränsade bakgrundsaktiviteter innehåller bakgrundsaktivitetens logik. I följande exempel:
- Tjänsten är asynkron. Metoden
DoWork
returnerar enTask
. I demonstrationssyfte väntar en fördröjning på tio sekunder iDoWork
metoden. - En ILogger matas in i tjänsten.
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);
}
}
}
Den värdbaserade tjänsten skapar ett omfång för att lösa den begränsade bakgrundsaktivitetstjänsten så att den kan anropa sin DoWork
metod.
DoWork
returnerar en Task
, som väntas i 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);
}
}
Tjänsterna är registrerade i IHostBuilder.ConfigureServices
(Program.cs
). Den värdbaserade tjänsten är registrerad med AddHostedService
tilläggsmetoden:
services.AddHostedService<ConsumeScopedServiceHostedService>();
services.AddScoped<IScopedProcessingService, ScopedProcessingService>();
Bakgrundsaktiviteter i kö
En bakgrundsaktivitetskö baseras på .NET Framework 4.x QueueBackgroundWorkItem:
public interface IBackgroundTaskQueue
{
ValueTask QueueBackgroundWorkItemAsync(Func<CancellationToken, ValueTask> workItem);
ValueTask<Func<CancellationToken, ValueTask>> DequeueAsync(
CancellationToken cancellationToken);
}
public class BackgroundTaskQueue : IBackgroundTaskQueue
{
private readonly Channel<Func<CancellationToken, ValueTask>> _queue;
public BackgroundTaskQueue(int capacity)
{
// Capacity should be set based on the expected application load and
// number of concurrent threads accessing the queue.
// BoundedChannelFullMode.Wait will cause calls to WriteAsync() to return a task,
// which completes only when space became available. This leads to backpressure,
// in case too many publishers/calls start accumulating.
var options = new BoundedChannelOptions(capacity)
{
FullMode = BoundedChannelFullMode.Wait
};
_queue = Channel.CreateBounded<Func<CancellationToken, ValueTask>>(options);
}
public async ValueTask QueueBackgroundWorkItemAsync(
Func<CancellationToken, ValueTask> workItem)
{
if (workItem == null)
{
throw new ArgumentNullException(nameof(workItem));
}
await _queue.Writer.WriteAsync(workItem);
}
public async ValueTask<Func<CancellationToken, ValueTask>> DequeueAsync(
CancellationToken cancellationToken)
{
var workItem = await _queue.Reader.ReadAsync(cancellationToken);
return workItem;
}
}
I följande QueueHostedService
exempel:
- Metoden
BackgroundProcessing
returnerar enTask
, som väntar iExecuteAsync
. - Bakgrundsuppgifter i kön tas bort och körs i
BackgroundProcessing
. - Arbetsobjekt väntar innan tjänsten stoppas i
StopAsync
.
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);
}
}
En MonitorLoop
tjänst hanterar lagringsuppgifter för den värdbaserade tjänsten när w
nyckeln väljs på en indataenhet:
-
IBackgroundTaskQueue
injiceras iMonitorLoop
-tjänsten. -
IBackgroundTaskQueue.QueueBackgroundWorkItem
anropas för att lägga till ett arbetsobjekt i kön. - Arbetsobjektet simulerar en långvarig bakgrundsaktivitet:
- Tre fördröjningar utförs på 5 sekunder vardera (
Task.Delay
). - En
try-catch
sats fångar OperationCanceledException om aktiviteten avbryts.
- Tre fördröjningar utförs på 5 sekunder vardera (
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("MonitorAsync Loop is starting.");
// Run a console user input loop in a background thread
Task.Run(async () => await MonitorAsync());
}
private async ValueTask MonitorAsync()
{
while (!_cancellationToken.IsCancellationRequested)
{
var keyStroke = Console.ReadKey();
if (keyStroke.Key == ConsoleKey.W)
{
// Enqueue a background work item
await _taskQueue.QueueBackgroundWorkItemAsync(BuildWorkItem);
}
}
}
private async ValueTask BuildWorkItem(CancellationToken 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);
}
}
}
Tjänsterna är registrerade i IHostBuilder.ConfigureServices
(Program.cs
). Den värdbaserade tjänsten är registrerad med AddHostedService
tilläggsmetoden:
services.AddSingleton<MonitorLoop>();
services.AddHostedService<QueuedHostedService>();
services.AddSingleton<IBackgroundTaskQueue>(ctx => {
if (!int.TryParse(hostContext.Configuration["QueueCapacity"], out var queueCapacity))
queueCapacity = 100;
return new BackgroundTaskQueue(queueCapacity);
});
MonitorLoop
startas i Program.Main
:
var monitorLoop = host.Services.GetRequiredService<MonitorLoop>();
monitorLoop.StartMonitorLoop();
Ytterligare resurser
ASP.NET Core