Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować się zalogować lub zmienić katalog.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Usługa kolejkowania to doskonały przykład usługi działającej przez dłuższy czas, w której elementy robocze można kolejkować i przetwarzane są sekwencyjnie, gdy poprzednie elementy robocze są ukończone. Korzystając z szablonu Worker Service, tworzysz nowe funkcje na wierzchu elementu BackgroundService.
W tym poradniku nauczysz się, jak:
- Utwórz usługę kolejki.
- Delegowanie pracy do kolejki zadań.
- Zarejestruj nasłuchiwacz klawiszy konsoli ze zdarzeń IHostApplicationLifetime.
Wskazówka
Cały przykładowy kod źródłowy "Pracownicy na platformie .NET" jest dostępny w przeglądarce Samples Browser do pobrania. Aby uzyskać więcej informacji, zobacz Przeglądanie przykładów kodu: Procesy robocze na platformie .NET.
Wymagania wstępne
- Zestaw SDK .NET 8.0 lub nowszy
- Zintegrowane środowisko projektowe (IDE) platformy .NET
- Zachęcam do korzystania z Visual Studio
Tworzenie nowego projektu
Aby utworzyć nowy projekt usługi roboczej za pomocą programu Visual Studio, wybierz plik>Nowy projekt>.... W oknie dialogowym Utwórz nowy projekt wyszukaj frazę "Usługa procesu roboczego" i wybierz pozycję Szablon usługi procesu roboczego. Jeśli wolisz użyć interfejsu wiersza polecenia platformy .NET, otwórz swój ulubiony terminal w katalogu roboczym. Uruchom polecenie dotnet new i zastąp <Project.Name> odpowiednią nazwą projektu.
dotnet new worker --name <Project.Name>
Aby uzyskać więcej informacji na temat polecenia nowego projektu usługi roboczej w CLI platformy .NET, zobacz dotnet new worker.
Wskazówka
Jeśli używasz programu Visual Studio Code, możesz uruchomić polecenia interfejsu wiersza polecenia platformy .NET z poziomu zintegrowanego terminalu. Aby uzyskać więcej informacji, zobacz Visual Studio Code: Integrated Terminal.
Tworzenie usług kolejkowania
Możesz być zaznajomiony z QueueBackgroundWorkItem(Func<CancellationToken,Task>) funkcjonalnością z System.Web.Hosting przestrzeni nazw.
Wskazówka
Funkcjonalność System.Web przestrzeni nazw celowo nie została przeniesiona do platformy .NET i pozostaje wyłączna dla platformy .NET Framework. Aby uzyskać więcej informacji, zobacz Wprowadzenie do migracji przyrostowej ASP.NET do ASP.NET Core.
Na platformie .NET, aby modelować usługę inspirowaną QueueBackgroundWorkItem funkcją, zacznij od dodania IBackgroundTaskQueue interfejsu do projektu:
namespace App.QueueService;
public interface IBackgroundTaskQueue
{
ValueTask QueueBackgroundWorkItemAsync(
Func<CancellationToken, ValueTask> workItem);
ValueTask<Func<CancellationToken, ValueTask>> DequeueAsync(
CancellationToken cancellationToken);
}
Istnieją dwie metody: jedna ujawnia funkcję kolejkowania, a druga usuwa wcześniej zakolejkowane elementy robocze.
Element roboczy to Func<CancellationToken, ValueTask>. Następnie dodaj domyślną implementację do projektu.
using System.Threading.Channels;
namespace App.QueueService;
public sealed class DefaultBackgroundTaskQueue : IBackgroundTaskQueue
{
private readonly Channel<Func<CancellationToken, ValueTask>> _queue;
public DefaultBackgroundTaskQueue(int capacity)
{
BoundedChannelOptions options = new(capacity)
{
FullMode = BoundedChannelFullMode.Wait
};
_queue = Channel.CreateBounded<Func<CancellationToken, ValueTask>>(options);
}
public async ValueTask QueueBackgroundWorkItemAsync(
Func<CancellationToken, ValueTask> workItem)
{
ArgumentNullException.ThrowIfNull(workItem);
await _queue.Writer.WriteAsync(workItem);
}
public async ValueTask<Func<CancellationToken, ValueTask>> DequeueAsync(
CancellationToken cancellationToken)
{
Func<CancellationToken, ValueTask>? workItem =
await _queue.Reader.ReadAsync(cancellationToken);
return workItem;
}
}
Poprzednia implementacja opiera się na kolejce Channel<T>. Element BoundedChannelOptions(Int32) jest wywoływany z określoną pojemnością. Pojemność powinna być ustawiana na podstawie oczekiwanego obciążenia aplikacji i liczby współbieżnych wątków, które uzyskują dostęp do kolejki. BoundedChannelFullMode.Wait powoduje, że wywołania do ChannelWriter<T>.WriteAsync zwracają zadanie, które kończy się dopiero wtedy, gdy miejsce stanie się dostępne. Co prowadzi do wstecznego ciśnienia, w przypadku gdy zbyt wielu wydawców/wywołań zaczynają się gromadzić.
Przepisz klasę Pracownik
W poniższym QueueHostedService przykładzie:
- Metoda
ProcessTaskQueueAsynczwraca Task wExecuteAsync. - Zadania w tle w kolejce są usuwane z kolejki i wykonywane w pliku
ProcessTaskQueueAsync. - Elementy robocze są oczekiwane przed zatrzymanie usługi w programie
StopAsync.
Zastąp istniejącą Worker klasę następującym kodem języka C# i zmień nazwę pliku na QueueHostedService.cs.
namespace App.QueueService;
public sealed class QueuedHostedService(
IBackgroundTaskQueue taskQueue,
ILogger<QueuedHostedService> logger) : BackgroundService
{
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
logger.LogInformation("""
{Name} is running.
Tap W to add a work item to the
background queue.
""",
nameof(QueuedHostedService));
return ProcessTaskQueueAsync(stoppingToken);
}
private async Task ProcessTaskQueueAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
try
{
Func<CancellationToken, ValueTask>? workItem =
await taskQueue.DequeueAsync(stoppingToken);
await workItem(stoppingToken);
}
catch (OperationCanceledException)
{
// Prevent throwing if stoppingToken was signaled
}
catch (Exception ex)
{
logger.LogError(ex, "Error occurred executing task work item.");
}
}
}
public override async Task StopAsync(CancellationToken stoppingToken)
{
logger.LogInformation(
$"{nameof(QueuedHostedService)} is stopping.");
await base.StopAsync(stoppingToken);
}
}
MonitorLoop Usługa obsługuje kolejkowanie zadań dla hostowanej usługi za każdym razem, gdy w klucz jest wybrany na urządzeniu wejściowym:
- Element
IBackgroundTaskQueuejest wstrzykiwany doMonitorLoopusługi. -
IBackgroundTaskQueue.QueueBackgroundWorkItemAsyncelement jest wywoływany w celu kolejkowania elementu roboczego. - Element roboczy symuluje długotrwałe zadanie w tle:
- Wykonywane są Delaytrzy 5-sekundowe opóźnienia.
- Instrukcja
try-catchwychwytuje OperationCanceledException , jeśli zadanie zostanie anulowane.
namespace App.QueueService;
public sealed class MonitorLoop(
IBackgroundTaskQueue taskQueue,
ILogger<MonitorLoop> logger,
IHostApplicationLifetime applicationLifetime)
{
private readonly CancellationToken _cancellationToken = applicationLifetime.ApplicationStopping;
public void StartMonitorLoop()
{
logger.LogInformation($"{nameof(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(BuildWorkItemAsync);
}
}
}
private async ValueTask BuildWorkItemAsync(CancellationToken token)
{
// Simulate three 5-second tasks to complete
// for each enqueued work item
int delayLoop = 0;
var guid = Guid.NewGuid();
logger.LogInformation("Queued work item {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 work item {Guid} is running. {DelayLoop}/3", guid, delayLoop);
}
if (delayLoop is 3)
{
logger.LogInformation("Queued Background Task {Guid} is complete.", guid);
}
else
{
logger.LogInformation("Queued Background Task {Guid} was cancelled.", guid);
}
}
}
Zastąp istniejącą Program zawartość następującym kodem języka C#:
using App.QueueService;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddSingleton<MonitorLoop>();
builder.Services.AddHostedService<QueuedHostedService>();
builder.Services.AddSingleton<IBackgroundTaskQueue>(_ =>
{
if (!int.TryParse(builder.Configuration["QueueCapacity"], out var queueCapacity))
{
queueCapacity = 100;
}
return new DefaultBackgroundTaskQueue(queueCapacity);
});
IHost host = builder.Build();
MonitorLoop monitorLoop = host.Services.GetRequiredService<MonitorLoop>()!;
monitorLoop.StartMonitorLoop();
host.Run();
Usługi są zarejestrowane w pliku (Program.cs). Hostowana usługa jest zarejestrowana za pomocą metody rozszerzenia AddHostedService.
MonitorLoop jest uruchamiany w Program.cs instrukcji najwyższego poziomu:
MonitorLoop monitorLoop = host.Services.GetRequiredService<MonitorLoop>()!;
monitorLoop.StartMonitorLoop();
Aby uzyskać więcej informacji na temat rejestrowania usług, zapoznaj się z wstrzykiwaniem zależności na platformie .NET.
Weryfikowanie funkcjonalności usługi
Aby uruchomić aplikację z poziomu programu Visual Studio, wybierz pozycję F5 lub wybierz opcję menu Debuguj>rozpocznij debugowanie . Jeśli korzystasz z .NET CLI, uruchom polecenie dotnet run z poziomu katalogu roboczego.
dotnet run
Aby uzyskać więcej informacji na temat polecenia uruchamiania interfejsu wiersza polecenia platformy .NET, zobacz dotnet run.
Gdy pojawi się monit, wprowadź w (lub W) co najmniej raz, aby dodać emulowany element roboczy do kolejki, jak pokazano na przykładzie w danych wyjściowych:
info: App.QueueService.MonitorLoop[0]
MonitorAsync loop is starting.
info: App.QueueService.QueuedHostedService[0]
QueuedHostedService is running.
Tap W to add a work item to the background queue.
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
Content root path: .\queue-service
winfo: App.QueueService.MonitorLoop[0]
Queued work item 8453f845-ea4a-4bcb-b26e-c76c0d89303e is starting.
info: App.QueueService.MonitorLoop[0]
Queued work item 8453f845-ea4a-4bcb-b26e-c76c0d89303e is running. 1/3
info: App.QueueService.MonitorLoop[0]
Queued work item 8453f845-ea4a-4bcb-b26e-c76c0d89303e is running. 2/3
info: App.QueueService.MonitorLoop[0]
Queued work item 8453f845-ea4a-4bcb-b26e-c76c0d89303e is running. 3/3
info: App.QueueService.MonitorLoop[0]
Queued Background Task 8453f845-ea4a-4bcb-b26e-c76c0d89303e is complete.
info: Microsoft.Hosting.Lifetime[0]
Application is shutting down...
info: App.QueueService.QueuedHostedService[0]
QueuedHostedService is stopping.
Jeśli uruchamiasz aplikację w programie Visual Studio, wybierz Zatrzymaj debugowanie…>. Alternatywnie wybierz Ctrl + C z okna konsoli, aby anulować.