Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
Készítette : Jeow Li Huan
Note
Ez nem a cikk legújabb verziója. Az aktuális kiadásról a cikk .NET 10-es verziójában olvashat.
Warning
A ASP.NET Core ezen verziója már nem támogatott. További információt a .NET és a .NET Core támogatási szabályzatában talál. Az aktuális kiadásról a cikk .NET 10-es verziójában olvashat.
A ASP.NET Core-ban a háttérfeladatok üzemeltetett szolgáltatásként implementálhatók. Az üzemeltetett szolgáltatás egy háttérfeladatlogikával rendelkező osztály, amely implementálja a IHostedService felületet. Ez a cikk három üzemeltetett szolgáltatás-példát tartalmaz:
- Időzítőn futó háttérfeladat.
- Üzemeltetett szolgáltatás, amely aktivál egy hatókörrel rendelkező szolgáltatást. A hatókörön belüli szolgáltatás függőséginjektálást (DI) használhat.
- Sorban álló háttérfeladatok, amelyek egymás után futnak.
Munkás szolgáltatás sablon
A ASP.NET Core Worker Service-sablon kiindulópontként szolgál a hosszú ideig futó szolgáltatásalkalmazások írásához. A Worker Service-sablonból létrehozott alkalmazás megadja a Worker SDK-t a projektfájlban:
<Project Sdk="Microsoft.NET.Sdk.Worker">
Üzemeltetett szolgáltatási alkalmazás alapjaként sablont használni:
- Új projekt létrehozása.
- Válassza a Munkás szolgáltatáslehetőséget. Válassza a Következőlehetőséget.
- Adjon meg egy projektnevet a Projektnév mezőben, vagy fogadja el az alapértelmezett projektnevet. Válassza a Következőlehetőséget.
- A További információ párbeszédpanelen válasszon keretrendszert. Válassza a Create gombot.
Package
A Worker Service-sablonon alapuló alkalmazások az SDK-t Microsoft.NET.Sdk.Worker használják, és kifejezetten hivatkoznak a Microsoft.Extensions.Hosting csomagra. Lásd például a mintaalkalmazás projektfájlját (BackgroundTasksSample.csproj).
Az SDK-t használó Microsoft.NET.Sdk.Web webalkalmazások esetében a Microsoft.Extensions.Hosting csomagra implicit módon hivatkozik a megosztott keretrendszer. Az alkalmazás projektfájljában nincs szükség explicit csomaghivatkozásra.
IHostedService felület
Az IHostedService interfész két metódust határoz meg a gazda által kezelt objektumokhoz.
StartAsync
A StartAsync (CancellationToken) tartalmazza a háttérfeladat indítására vonatkozó logikát.
StartAsync a kezdet előtt van meghívva:
- Az alkalmazás kérésfeldolgozási folyamata konfigurálva van.
- A kiszolgáló elindul, és az IApplicationLifetime.ApplicationStarted aktiválódik.
StartAsync rövid ideig futó tevékenységekre kell korlátozódnia, mert az üzemeltetett szolgáltatások egymás után futnak, és a futtatások befejezéséig StartAsync nem indulnak el további szolgáltatások.
StopAsync
-
StopAsync(CancellationToken) akkor aktiválódik, amikor a gazdagép kíméletes leállítást végez.
StopAsyncA háttérfeladat befejezéséhez használni kívánt logikát tartalmazza. Implementálja a IDisposable és véglegesítőket (destruktorokat), hogy felszabadítsa a nem felügyelt erőforrásokat.
A megszakítási token alapértelmezett 30 másodperces időkorlátot alkalmaz annak jelzésére, hogy a leállítási folyamat már nem folytatódhat zökkenőmentesen. Amikor lemondást kérnek a tokenre:
- Az alkalmazás által végrehajtott többi háttérműveletet le kell szakítani.
- Minden behívott
StopAsyncmetódusnak azonnal vissza kell térnie.
A lemondás kérése után azonban a feladatok nem lesznek félbehagyva – a hívó minden feladat befejezésére vár.
Ha az alkalmazás váratlanul leáll (például az alkalmazás folyamata meghiúsul), StopAsync lehet, hogy nem lesz meghívva. Ezért előfordulhat, hogy az StopAsync meghívott metódusok vagy végrehajtott műveletek nem valósulnak meg.
Az alapértelmezett 30 másodperces leállítási időtúllépés meghosszabbításához állítsa be a következőt:
- ShutdownTimeout Generic Host használata esetén. További információért lásd: .NET Generic Host az ASP.NET Core-ban.
- A leállítási időkorlát kiszolgáló konfigurációs beállítása a webkiszolgáló használatakor. További információ: ASP.NET Core Web Host.
Az üzemeltetett szolgáltatás az alkalmazás indításakor egyszer aktiválódik, és az alkalmazás leállításakor kecsesen leáll. Ha a háttérben futó feladat végrehajtása során hibaüzenet jelenik meg, akkor is meg kell hívni, Dispose ha StopAsync nincs meghívva.
BackgroundService alaposztály
BackgroundService egy alaposztály egy hosszú ideig futó IHostedServiceimplementáláshoz.
Az ExecuteAsync (CancellationToken) parancs a háttérszolgáltatás futtatására van meghívva. Az implementáció egy olyan értéket Task ad vissza, amely a háttérszolgáltatás teljes élettartamát képviseli. A rendszer nem indít el további szolgáltatásokat, amíg az ExecuteAsync aszinkronná nem válik, például a hívással await. Kerülje a hosszú, blokkoló inicializáló munkát a ExecuteAsync-ban. A StopAsync(CancellationToken) esetén a gazda arra vár, hogy a ExecuteAsync befejeződjön.
A lemondási token az IHostedService.StopAsync hívásakor aktiválódik. Az Ön ExecuteAsync implementációjának azonnal be kell fejeződnie, amikor a megszakítási token aktiválódik, hogy a szolgáltatást megfelelően leállítsa. Ellenkező esetben a szolgáltatás kényszerítetten leáll a kikapcsolási időtúllépéskor. További információkért lásd az IHostedService felületének szakaszát.
További információ: BackgroundService forráskód.
Időzított háttérfeladatok
Az időzított háttérfeladatok a System.Threading.Timer osztályt használják . Az időzítő aktiválja a tevékenység metódusát DoWork . Az időzítő le van tiltva StopAsync, és ártalmatlanítva van, amikor a szolgáltatástároló ártalmatlanításra kerül 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();
}
}
A Timer rendszer nem várja meg, amíg a korábbi végrehajtások DoWork befejeződnek, ezért előfordulhat, hogy a bemutatott megközelítés nem minden forgatókönyvhöz megfelelő.
Az Interlocked.Increment a végrehajtási számláló atomi műveletként történő növelésére szolgál, amely biztosítja, hogy több szál ne frissüljön executionCount egyidejűleg.
A szolgáltatás IHostBuilder.ConfigureServices regisztrálva van (Program.cs) a AddHostedService bővítménymetódussal.
services.AddHostedService<TimedHostedService>();
Háttérfeladatban egy hatókörű szolgáltatás használata
Ha hatókörrel rendelkező szolgáltatásokat szeretne használni a BackgroundService szolgáltatásban, hozzon létre egy hatókört. A rendszer alapértelmezés szerint nem hoz létre hatókört egy üzemeltetett szolgáltatáshoz.
A hatókörrel rendelkező háttérfeladat-szolgáltatás tartalmazza a háttértevékenység logikáját. Az alábbi példában:
- A szolgáltatás aszinkron. A
DoWorkmetódus egyTask. Bemutatás céljából aDoWorkmódszer esetében tíz másodperces késésre van szükség. - A szolgáltatásba injektálunk egy ILogger elemet.
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);
}
}
}
Az üzemeltetett szolgáltatás létrehoz egy hatókört, amely megoldja a hatókörrel rendelkező háttérfeladat-szolgáltatást annak DoWork metódusának meghívásához.
DoWork visszaad egy Task-t, amelyet ExecuteAsync-ben várnak:
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);
}
}
A szolgáltatások regisztrálva vannak IHostBuilder.ConfigureServices (Program.cs). A üzemeltetett szolgáltatás regisztrálva van a AddHostedService bővítménymetódussal:
services.AddHostedService<ConsumeScopedServiceHostedService>();
services.AddScoped<IScopedProcessingService, ScopedProcessingService>();
Várólistán lévő háttérfeladatok
A háttérfeladat-üzenetsor a .NET-keretrendszer 4.x-en QueueBackgroundWorkItemalapul:
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;
}
}
Az alábbi QueueHostedService példában:
- A
BackgroundProcessingmetódus egyTask-t ad vissza, amit aExecuteAsync-ben várunk. - Az üzenetsor háttérfeladatai le lesznek kérdezve és végrehajtva a következőben
BackgroundProcessing: . - A munkaelemeket a szolgáltatás leállása előtt várjuk.
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);
}
}
A MonitorLoop szolgáltatás kezeli az üzemeltetett szolgáltatás lekérdezési feladatait, amikor a w kulcs ki van választva egy bemeneti eszközön:
- A
IBackgroundTaskQueueaMonitorLoopszolgáltatásba lett beszúrva. -
IBackgroundTaskQueue.QueueBackgroundWorkItemmeghívásra kerül egy munkaelem sorba állításához. - A munkaelem egy hosszú ideig futó háttérfeladatot szimulál:
- A rendszer három 5 másodperces késést hajt végre (
Task.Delay). - Az
try-catchutasítás elkapja a OperationCanceledException-t, ha a feladat lemondásra kerül.
- A rendszer három 5 másodperces késést hajt végre (
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);
}
}
}
A szolgáltatások regisztrálva vannak IHostBuilder.ConfigureServices (Program.cs). A üzemeltetett szolgáltatás regisztrálva van a AddHostedService bővítménymetódussal:
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 a következőben Program.csindult el:
var monitorLoop = host.Services.GetRequiredService<MonitorLoop>();
monitorLoop.StartMonitorLoop();
Aszinkron időzített háttérfeladat
A következő kód egy aszinkron időzített háttérfeladatot hoz létre:
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);
}
}
Natív AOT
A Feldolgozói szolgáltatás sablonjai támogatják a .NET natív előzetes fordítást (AOT) a --aot megadott jelölővel.
- Új projekt létrehozása.
- Válassza a Munkás szolgáltatáslehetőséget. Válassza a Következőlehetőséget.
- Adjon meg egy projektnevet a Projektnév mezőben, vagy fogadja el az alapértelmezett projektnevet. Válassza a Következőlehetőséget.
- A További információk párbeszédpanelen:
- Válasszon egy keretrendszert.
- Jelölje be a Natív AOT-közzététel engedélyezése jelölőnégyzetet.
- Válassza a Create gombot.
Az AOT-beállítás hozzáadja a <PublishAot>true</PublishAot> elemet a projektfájlhoz:
<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>
További erőforrások
- Háttérszolgáltatások egységtesztjei a GitHubon.
- Mintakód megtekintése vagy letöltése (hogyan töltsd le)
- Háttérfeladatok implementálása mikroszolgáltatásokban az IHostedService és a BackgroundService osztály használatával
- Háttérfeladatok futtatása a WebJobs használatával Azure-alkalmazás szolgáltatásban
- Timer
A ASP.NET Core-ban a háttérfeladatok üzemeltetett szolgáltatásként implementálhatók. Az üzemeltetett szolgáltatás egy háttérfeladatlogikával rendelkező osztály, amely implementálja a IHostedService felületet. Ez a cikk három üzemeltetett szolgáltatás-példát tartalmaz:
- Időzítőn futó háttérfeladat.
- Üzemeltetett szolgáltatás, amely aktivál egy hatókörrel rendelkező szolgáltatást. A hatókörön belüli szolgáltatás függőséginjektálást (DI) használhat.
- Sorban álló háttérfeladatok, amelyek egymás után futnak.
Munkás szolgáltatás sablon
A ASP.NET Core Worker Service-sablon kiindulópontként szolgál a hosszú ideig futó szolgáltatásalkalmazások írásához. A Worker Service-sablonból létrehozott alkalmazás megadja a Worker SDK-t a projektfájlban:
<Project Sdk="Microsoft.NET.Sdk.Worker">
Üzemeltetett szolgáltatási alkalmazás alapjaként sablont használni:
- Új projekt létrehozása.
- Válassza a Munkás szolgáltatáslehetőséget. Válassza a Következőlehetőséget.
- Adjon meg egy projektnevet a Projektnév mezőben, vagy fogadja el az alapértelmezett projektnevet. Válassza a Következőlehetőséget.
- A További információ párbeszédpanelen válasszon keretrendszert. Válassza a Create gombot.
Package
A Worker Service-sablonon alapuló alkalmazások az SDK-t Microsoft.NET.Sdk.Worker használják, és kifejezetten hivatkoznak a Microsoft.Extensions.Hosting csomagra. Lásd például a mintaalkalmazás projektfájlját (BackgroundTasksSample.csproj).
Az SDK-t használó Microsoft.NET.Sdk.Web webalkalmazások esetében a Microsoft.Extensions.Hosting csomagra implicit módon hivatkozik a megosztott keretrendszer. Az alkalmazás projektfájljában nincs szükség explicit csomaghivatkozásra.
IHostedService felület
Az IHostedService interfész két metódust határoz meg a gazda által kezelt objektumokhoz.
StartAsync
A StartAsync (CancellationToken) tartalmazza a háttérfeladat indítására vonatkozó logikát.
StartAsync a kezdet előtt van meghívva:
- Az alkalmazás kérésfeldolgozási folyamata konfigurálva van.
- A kiszolgáló elindul, és az IApplicationLifetime.ApplicationStarted aktiválódik.
StartAsync rövid ideig futó tevékenységekre kell korlátozódnia, mert az üzemeltetett szolgáltatások egymás után futnak, és a futtatások befejezéséig StartAsync nem indulnak el további szolgáltatások.
StopAsync
-
StopAsync(CancellationToken) akkor aktiválódik, amikor a gazdagép kíméletes leállítást végez.
StopAsyncA háttérfeladat befejezéséhez használni kívánt logikát tartalmazza. Implementálja a IDisposable és véglegesítőket (destruktorokat), hogy felszabadítsa a nem felügyelt erőforrásokat.
A megszakítási token alapértelmezett 30 másodperces időkorlátot alkalmaz annak jelzésére, hogy a leállítási folyamat már nem folytatódhat zökkenőmentesen. Amikor lemondást kérnek a tokenre:
- Az alkalmazás által végrehajtott többi háttérműveletet le kell szakítani.
- Minden behívott
StopAsyncmetódusnak azonnal vissza kell térnie.
A lemondás kérése után azonban a feladatok nem lesznek félbehagyva – a hívó minden feladat befejezésére vár.
Ha az alkalmazás váratlanul leáll (például az alkalmazás folyamata meghiúsul), StopAsync lehet, hogy nem lesz meghívva. Ezért előfordulhat, hogy az StopAsync meghívott metódusok vagy végrehajtott műveletek nem valósulnak meg.
Az alapértelmezett 30 másodperces leállítási időtúllépés meghosszabbításához állítsa be a következőt:
- ShutdownTimeout Generic Host használata esetén. További információért lásd: .NET Generic Host az ASP.NET Core-ban.
- A leállítási időkorlát kiszolgáló konfigurációs beállítása a webkiszolgáló használatakor. További információ: ASP.NET Core Web Host.
Az üzemeltetett szolgáltatás az alkalmazás indításakor egyszer aktiválódik, és az alkalmazás leállításakor kecsesen leáll. Ha a háttérben futó feladat végrehajtása során hibaüzenet jelenik meg, akkor is meg kell hívni, Dispose ha StopAsync nincs meghívva.
BackgroundService alaposztály
BackgroundService egy alaposztály egy hosszú ideig futó IHostedServiceimplementáláshoz.
Az ExecuteAsync (CancellationToken) parancs a háttérszolgáltatás futtatására van meghívva. Az implementáció egy olyan értéket Task ad vissza, amely a háttérszolgáltatás teljes élettartamát képviseli. A rendszer nem indít el további szolgáltatásokat, amíg az ExecuteAsync aszinkronná nem válik, például a hívással await. Kerülje a hosszú, blokkoló inicializáló munkát a ExecuteAsync-ban. A StopAsync(CancellationToken) esetén a gazda arra vár, hogy a ExecuteAsync befejeződjön.
A lemondási token az IHostedService.StopAsync hívásakor aktiválódik. Az Ön ExecuteAsync implementációjának azonnal be kell fejeződnie, amikor a megszakítási token aktiválódik, hogy a szolgáltatást megfelelően leállítsa. Ellenkező esetben a szolgáltatás kényszerítetten leáll a kikapcsolási időtúllépéskor. További információkért lásd az IHostedService felületének szakaszát.
További információ: BackgroundService forráskód.
Időzított háttérfeladatok
Az időzított háttérfeladatok a System.Threading.Timer osztályt használják . Az időzítő aktiválja a tevékenység metódusát DoWork . Az időzítő le van tiltva StopAsync, és ártalmatlanítva van, amikor a szolgáltatástároló ártalmatlanításra kerül 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();
}
}
A Timer rendszer nem várja meg, amíg a korábbi végrehajtások DoWork befejeződnek, ezért előfordulhat, hogy a bemutatott megközelítés nem minden forgatókönyvhöz megfelelő.
Az Interlocked.Increment a végrehajtási számláló atomi műveletként történő növelésére szolgál, amely biztosítja, hogy több szál ne frissüljön executionCount egyidejűleg.
A szolgáltatás IHostBuilder.ConfigureServices regisztrálva van (Program.cs) a AddHostedService bővítménymetódussal.
services.AddHostedService<TimedHostedService>();
Háttérfeladatban egy hatókörű szolgáltatás használata
Ha hatókörrel rendelkező szolgáltatásokat szeretne használni a BackgroundService szolgáltatásban, hozzon létre egy hatókört. A rendszer alapértelmezés szerint nem hoz létre hatókört egy üzemeltetett szolgáltatáshoz.
A hatókörrel rendelkező háttérfeladat-szolgáltatás tartalmazza a háttértevékenység logikáját. Az alábbi példában:
- A szolgáltatás aszinkron. A
DoWorkmetódus egyTask. Bemutatás céljából aDoWorkmódszer esetében tíz másodperces késésre van szükség. - A szolgáltatásba injektálunk egy ILogger elemet.
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);
}
}
}
Az üzemeltetett szolgáltatás létrehoz egy hatókört, amely megoldja a hatókörrel rendelkező háttérfeladat-szolgáltatást annak DoWork metódusának meghívásához.
DoWork visszaad egy Task-t, amelyet ExecuteAsync-ben várnak:
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);
}
}
A szolgáltatások regisztrálva vannak IHostBuilder.ConfigureServices (Program.cs). A üzemeltetett szolgáltatás regisztrálva van a AddHostedService bővítménymetódussal:
services.AddHostedService<ConsumeScopedServiceHostedService>();
services.AddScoped<IScopedProcessingService, ScopedProcessingService>();
Várólistán lévő háttérfeladatok
A háttérfeladat-üzenetsor a .NET-keretrendszer 4.x-en QueueBackgroundWorkItemalapul:
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;
}
}
Az alábbi QueueHostedService példában:
- A
BackgroundProcessingmetódus egyTask-t ad vissza, amit aExecuteAsync-ben várunk. - Az üzenetsor háttérfeladatai le lesznek kérdezve és végrehajtva a következőben
BackgroundProcessing: . - A munkaelemeket a szolgáltatás leállása előtt várjuk.
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);
}
}
A MonitorLoop szolgáltatás kezeli az üzemeltetett szolgáltatás lekérdezési feladatait, amikor a w kulcs ki van választva egy bemeneti eszközön:
- A
IBackgroundTaskQueueaMonitorLoopszolgáltatásba lett beszúrva. -
IBackgroundTaskQueue.QueueBackgroundWorkItemmeghívásra kerül egy munkaelem sorba állításához. - A munkaelem egy hosszú ideig futó háttérfeladatot szimulál:
- A rendszer három 5 másodperces késést hajt végre (
Task.Delay). - Az
try-catchutasítás elkapja a OperationCanceledException-t, ha a feladat lemondásra kerül.
- A rendszer három 5 másodperces késést hajt végre (
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);
}
}
}
A szolgáltatások regisztrálva vannak IHostBuilder.ConfigureServices (Program.cs). A üzemeltetett szolgáltatás regisztrálva van a AddHostedService bővítménymetódussal:
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 a következőben Program.csindult el:
var monitorLoop = host.Services.GetRequiredService<MonitorLoop>();
monitorLoop.StartMonitorLoop();
Aszinkron időzített háttérfeladat
A következő kód egy aszinkron időzített háttérfeladatot hoz létre:
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);
}
}
További erőforrások
- Háttérszolgáltatások egységtesztjei a GitHubon.
- Mintakód megtekintése vagy letöltése (hogyan töltsd le)
- Háttérfeladatok implementálása mikroszolgáltatásokban az IHostedService és a BackgroundService osztály használatával
- Háttérfeladatok futtatása a WebJobs használatával Azure-alkalmazás szolgáltatásban
- Timer
A ASP.NET Core-ban a háttérfeladatok üzemeltetett szolgáltatásként implementálhatók. Az üzemeltetett szolgáltatás egy háttérfeladatlogikával rendelkező osztály, amely implementálja a IHostedService felületet. Ez a cikk három üzemeltetett szolgáltatás-példát tartalmaz:
- Időzítőn futó háttérfeladat.
- Üzemeltetett szolgáltatás, amely aktivál egy hatókörrel rendelkező szolgáltatást. A hatókörön belüli szolgáltatás függőséginjektálást (DI) használhat.
- Sorban álló háttérfeladatok, amelyek egymás után futnak.
Mintakód megtekintése vagy letöltése (hogyan töltsd le)
Munkás szolgáltatás sablon
A ASP.NET Core Worker Service-sablon kiindulópontként szolgál a hosszú ideig futó szolgáltatásalkalmazások írásához. A Worker Service-sablonból létrehozott alkalmazás megadja a Worker SDK-t a projektfájlban:
<Project Sdk="Microsoft.NET.Sdk.Worker">
Üzemeltetett szolgáltatási alkalmazás alapjaként sablont használni:
- Új projekt létrehozása.
- Válassza a Munkás szolgáltatáslehetőséget. Válassza a Következőlehetőséget.
- Adjon meg egy projektnevet a Projektnév mezőben, vagy fogadja el az alapértelmezett projektnevet. Válassza a Create gombot.
- Az Új feldolgozói szolgáltatás létrehozása párbeszédpanelen válassza a létrehozása lehetőséget.
Package
A Worker Service-sablonon alapuló alkalmazások az SDK-t Microsoft.NET.Sdk.Worker használják, és kifejezetten hivatkoznak a Microsoft.Extensions.Hosting csomagra. Lásd például a mintaalkalmazás projektfájlját (BackgroundTasksSample.csproj).
Az SDK-t használó Microsoft.NET.Sdk.Web webalkalmazások esetében a Microsoft.Extensions.Hosting csomagra implicit módon hivatkozik a megosztott keretrendszer. Az alkalmazás projektfájljában nincs szükség explicit csomaghivatkozásra.
IHostedService felület
Az IHostedService interfész két metódust határoz meg a gazda által kezelt objektumokhoz.
StartAsync
StartAsync A háttérfeladat indításához használni kívánt logikát tartalmazza.
StartAsync a kezdet előtt van meghívva:
- Az alkalmazás kérésfeldolgozási folyamata konfigurálva van.
- A kiszolgáló elindul, és az IApplicationLifetime.ApplicationStarted aktiválódik.
Az alapértelmezett viselkedés módosítható úgy, hogy az üzemeltetett szolgáltatás StartAsync az alkalmazás folyamatának konfigurálása és ApplicationStarted meghívása után fusson. Az alapértelmezett viselkedés módosításához, adja hozzá a hívás VideosWatcher után az üzemeltetett szolgáltatást (ConfigureWebHostDefaults az alábbi példában).
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) akkor aktiválódik, amikor a gazdagép kíméletes leállítást végez.
StopAsyncA háttérfeladat befejezéséhez használni kívánt logikát tartalmazza. Implementálja a IDisposable és véglegesítőket (destruktorokat), hogy felszabadítsa a nem felügyelt erőforrásokat.
A törlési token alapértelmezett öt másodperces időtúllépéssel rendelkezik, amely azt jelzi, hogy a leállítási folyamat ne legyen többé zökkenőmentes. Amikor lemondást kérnek a tokenre:
- Az alkalmazás által végrehajtott többi háttérműveletet le kell szakítani.
- Minden behívott
StopAsyncmetódusnak azonnal vissza kell térnie.
A lemondás kérése után azonban a feladatok nem lesznek félbehagyva – a hívó minden feladat befejezésére vár.
Ha az alkalmazás váratlanul leáll (például az alkalmazás folyamata meghiúsul), StopAsync lehet, hogy nem lesz meghívva. Ezért előfordulhat, hogy az StopAsync meghívott metódusok vagy végrehajtott műveletek nem valósulnak meg.
Az alapértelmezett öt másodperces leállítási időtúllépés meghosszabbításához állítsa be a következőt:
- ShutdownTimeout Generic Host használata esetén. További információért lásd: .NET Generic Host az ASP.NET Core-ban.
- A leállítási időkorlát kiszolgáló konfigurációs beállítása a webkiszolgáló használatakor. További információ: ASP.NET Core Web Host.
Az üzemeltetett szolgáltatás az alkalmazás indításakor egyszer aktiválódik, és az alkalmazás leállításakor kecsesen leáll. Ha a háttérben futó feladat végrehajtása során hibaüzenet jelenik meg, akkor is meg kell hívni, Dispose ha StopAsync nincs meghívva.
BackgroundService alaposztály
BackgroundService egy alaposztály egy hosszú ideig futó IHostedServiceimplementáláshoz.
Az ExecuteAsync (CancellationToken) parancs a háttérszolgáltatás futtatására van meghívva. Az implementáció egy olyan értéket Task ad vissza, amely a háttérszolgáltatás teljes élettartamát képviseli. A rendszer nem indít el további szolgáltatásokat, amíg az ExecuteAsync aszinkronná nem válik, például a hívással await. Kerülje a hosszú, blokkoló inicializáló munkát a ExecuteAsync-ban. A StopAsync(CancellationToken) esetén a gazda arra vár, hogy a ExecuteAsync befejeződjön.
A lemondási token az IHostedService.StopAsync hívásakor aktiválódik. Az Ön ExecuteAsync implementációjának azonnal be kell fejeződnie, amikor a megszakítási token aktiválódik, hogy a szolgáltatást megfelelően leállítsa. Ellenkező esetben a szolgáltatás kényszerítetten leáll a kikapcsolási időtúllépéskor. További információkért lásd az IHostedService felületének szakaszát.
StartAsync rövid ideig futó tevékenységekre kell korlátozódnia, mert az üzemeltetett szolgáltatások egymás után futnak, és a futtatások befejezéséig StartAsync nem indulnak el további szolgáltatások. A hosszú ideig futó feladatokat be kell helyezni a fájlba ExecuteAsync. További információt a BackgroundService forrásában talál.
Időzított háttérfeladatok
Az időzított háttérfeladatok a System.Threading.Timer osztályt használják . Az időzítő aktiválja a tevékenység metódusát DoWork . Az időzítő le van tiltva StopAsync, és ártalmatlanítva van, amikor a szolgáltatástároló ártalmatlanításra kerül 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();
}
}
A Timer rendszer nem várja meg, amíg a korábbi végrehajtások DoWork befejeződnek, ezért előfordulhat, hogy a bemutatott megközelítés nem minden forgatókönyvhöz megfelelő.
Az Interlocked.Increment a végrehajtási számláló atomi műveletként történő növelésére szolgál, amely biztosítja, hogy több szál ne frissüljön executionCount egyidejűleg.
A szolgáltatás IHostBuilder.ConfigureServices regisztrálva van (Program.cs) a AddHostedService bővítménymetódussal.
services.AddHostedService<TimedHostedService>();
Háttérfeladatban egy hatókörű szolgáltatás használata
Ha hatókörrel rendelkező szolgáltatásokat szeretne használni a BackgroundService szolgáltatásban, hozzon létre egy hatókört. A rendszer alapértelmezés szerint nem hoz létre hatókört egy üzemeltetett szolgáltatáshoz.
A hatókörrel rendelkező háttérfeladat-szolgáltatás tartalmazza a háttértevékenység logikáját. Az alábbi példában:
- A szolgáltatás aszinkron. A
DoWorkmetódus egyTask. Bemutatás céljából aDoWorkmódszer esetében tíz másodperces késésre van szükség. - A szolgáltatásba injektálunk egy ILogger elemet.
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);
}
}
}
Az üzemeltetett szolgáltatás létrehoz egy hatókört, amely megoldja a hatókörrel rendelkező háttérfeladat-szolgáltatást annak DoWork metódusának meghívásához.
DoWork visszaad egy Task-t, amelyet ExecuteAsync-ben várnak:
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);
}
}
A szolgáltatások regisztrálva vannak IHostBuilder.ConfigureServices (Program.cs). A üzemeltetett szolgáltatás regisztrálva van a AddHostedService bővítménymetódussal:
services.AddHostedService<ConsumeScopedServiceHostedService>();
services.AddScoped<IScopedProcessingService, ScopedProcessingService>();
Várólistán lévő háttérfeladatok
A háttérfeladat-üzenetsor a .NET-keretrendszer 4.x-en QueueBackgroundWorkItemalapul:
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;
}
}
Az alábbi QueueHostedService példában:
- A
BackgroundProcessingmetódus egyTask-t ad vissza, amit aExecuteAsync-ben várunk. - Az üzenetsor háttérfeladatai le lesznek kérdezve és végrehajtva a következőben
BackgroundProcessing: . - A munkaelemeket a szolgáltatás leállása előtt várjuk.
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);
}
}
A MonitorLoop szolgáltatás kezeli az üzemeltetett szolgáltatás lekérdezési feladatait, amikor a w kulcs ki van választva egy bemeneti eszközön:
- A
IBackgroundTaskQueueaMonitorLoopszolgáltatásba lett beszúrva. -
IBackgroundTaskQueue.QueueBackgroundWorkItemmeghívásra kerül egy munkaelem sorba állításához. - A munkaelem egy hosszú ideig futó háttérfeladatot szimulál:
- A rendszer három 5 másodperces késést hajt végre (
Task.Delay). - Az
try-catchutasítás elkapja a OperationCanceledException-t, ha a feladat lemondásra kerül.
- A rendszer három 5 másodperces késést hajt végre (
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);
}
}
}
A szolgáltatások regisztrálva vannak IHostBuilder.ConfigureServices (Program.cs). A üzemeltetett szolgáltatás regisztrálva van a AddHostedService bővítménymetódussal:
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 a következőben Program.Mainindult el:
var monitorLoop = host.Services.GetRequiredService<MonitorLoop>();
monitorLoop.StartMonitorLoop();