Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Frontová služba je skvělým příkladem dlouhotrvající služby, kde lze pracovní položky zařadit do fronty a postupně na nich pracovat, jakmile jsou dokončeny předchozí pracovní položky. S využitím šablony pracovní služby vytvoříte novou funkcionalitu na základě BackgroundService.
V tomto návodu se naučíte, jak:
- Vytvořte službu fronty.
- Delegujte práci do fronty úloh.
- Zaregistrujte nasloucháč kláves konzole pro události IHostApplicationLifetime.
Návod
Všechny ukázkové zdrojové kódy Pracovních procesů v .NET jsou k dispozici v prohlížeči ukázek ke stažení. Další informace najdete v tématu Procházení ukázek kódu: Pracovní procesy v .NET.
Požadavky
- Sada .NET 8.0 SDK nebo novější
- Integrované vývojové prostředí .NET (IDE)
- Neváhejte používat visual studio
Vytvoření nového projektu
Pokud chcete vytvořit nový projekt Služby pracovního procesu pomocí sady Visual Studio, vyberte Soubor>Nový projekt>.... V dialogovém okně Vytvořit nový projekt vyhledejte "Pracovní služba" a vyberte šablonu pracovní služby. Pokud raději použijete .NET CLI, otevřete svůj oblíbený terminál v pracovním adresáři. Spusťte příkaz dotnet new a nahraďte <Project.Name> názvem požadovaného projektu.
dotnet new worker --name <Project.Name>
Další informace o příkazu dotnet new worker, který vytvoří nový projekt pracovní služby v rozhraní příkazového řádku .NET CLI, najdete v tématu.
Návod
Pokud používáte Visual Studio Code, můžete z integrovaného terminálu spustit příkazy .NET CLI. Další informace naleznete v tématu Visual Studio Code: Integrovaný terminál.
Vytvoření služeb řazení do front
Možná znáte funkcionalitu QueueBackgroundWorkItem(Func<CancellationToken,Task>) z oboru názvů System.Web.Hosting.
Návod
Funkcionalita jmenného prostoru System.Web nebyla úmyslně přenesena do .NET a zůstává výhradní pro rozhraní .NET Framework. Další informace najdete v tématu Úvod do přírůstkové migrace z ASP.NET na ASP.NET Core.
Pokud chcete v .NET modelovat službu inspirovanou QueueBackgroundWorkItem funkcí, začněte přidáním IBackgroundTaskQueue rozhraní do projektu:
namespace App.QueueService;
public interface IBackgroundTaskQueue
{
ValueTask QueueBackgroundWorkItemAsync(
Func<CancellationToken, ValueTask> workItem);
ValueTask<Func<CancellationToken, ValueTask>> DequeueAsync(
CancellationToken cancellationToken);
}
Existují dvě metody, jedna, která zpřístupňuje funkce fronty, a druhá, která odstraní z fronty dříve zařazené pracovní položky.
Pracovní položka je .Func<CancellationToken, ValueTask> Dále přidejte do projektu výchozí implementaci.
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;
}
}
Předchozí implementace spoléhá na Channel<T> jako frontu. Volá se BoundedChannelOptions(Int32) s jasně stanovenou kapacitou. Kapacita by měla být nastavená na základě očekávaného zatížení aplikace a počtu souběžných vláken přistupujících k frontě. BoundedChannelFullMode.Wait způsobí, že volání ChannelWriter<T>.WriteAsync vrátí úlohu, která se dokončí pouze tehdy, když se stane dostupným místo. To vede k protitlaku v případě, že se začne hromadit příliš mnoho vydavatelů nebo volání.
Přepište třídu Worker
V následujícím QueueHostedService příkladu:
- Metoda
ProcessTaskQueueAsyncvrátí hodnotu Task inExecuteAsync. - Úlohy na pozadí ve frontě jsou vyřazeny z fronty a spuštěny v
ProcessTaskQueueAsync. - Pracovní položky jsou očekávána před zastavením
StopAsyncslužby .
Nahraďte existující Worker třídu následujícím kódem jazyka C# a přejmenujte soubor 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 Služba zpracovává úlohy zařazení do fronty hostované služby při každém w výběru klíče na vstupním zařízení:
- Vloží se
IBackgroundTaskQueuedoMonitorLoopslužby. -
IBackgroundTaskQueue.QueueBackgroundWorkItemAsyncje volána k vytvoření fronty pracovní položky. - Pracovní položka simuluje dlouhotrvající úlohu na pozadí:
- Provádí se tři zpoždění po 5 sekundáchDelay.
- Příkaz
try-catchzachytí OperationCanceledException, pokud je úloha zrušena.
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);
}
}
}
Nahraďte existující Program obsah následujícím kódem jazyka 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();
Služby jsou zaregistrované v (Program.cs). Hostovaná služba je zaregistrovaná v AddHostedService metodě rozšíření.
MonitorLoop je spuštěn v Program.cs příkazu nejvyšší úrovně:
MonitorLoop monitorLoop = host.Services.GetRequiredService<MonitorLoop>()!;
monitorLoop.StartMonitorLoop();
Další informace o registraci služeb naleznete v tématu Injektáž závislostí v rozhraní .NET.
Ověření funkčnosti služby
Pokud chcete aplikaci spustit ze sady Visual Studio, vyberte F5 nebo vyberte možnost nabídky Ladit>Spustit ladění. Pokud používáte .NET CLI, spusťte dotnet run příkaz z pracovního adresáře:
dotnet run
Další informace o příkazu pro spuštění .NET CLI najdete v tématu dotnet run.
Po zobrazení výzvy zadejte alespoň jednou w (nebo W) pro zařazení emulované pracovní položky do fronty, jak je znázorněno v příkladu výstupu:
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.
Pokud aplikaci spouštíte ze sady Visual Studio, vyberte Ladění>zastavit ladění.... Případně můžete výběrem kláves Ctrl + C z okna konzoly signalizovat zrušení.