Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Oleh Jeow Li Huan
Note
Ini bukan versi terbaru dari artikel ini. Untuk rilis saat ini, lihat versi .NET 10 dari artikel ini.
Warning
Versi ASP.NET Core ini tidak lagi didukung. Untuk informasi selengkapnya, lihat Kebijakan Dukungan .NET dan .NET Core. Untuk rilis saat ini, lihat versi .NET 10 dari artikel ini.
Di ASP.NET Core, tugas latar belakang dapat diimplementasikan sebagai layanan yang dihosting. Layanan yang dihosting adalah kelas dengan logika tugas latar belakang yang mengimplementasikan IHostedService antarmuka. Artikel ini menyediakan tiga contoh layanan yang dihosting:
- Tugas latar belakang yang berjalan pada timer.
- Layanan yang dihosting yang mengaktifkan layanan terlingkup. Layanan tercakup dapat menggunakan injeksi dependensi (DI).
- Tugas latar belakang antrean yang berjalan secara berurutan.
Templat Layanan Pekerja
Templat Layanan Pekerja Inti ASP.NET menyediakan titik awal untuk menulis aplikasi layanan yang berjalan lama. Aplikasi yang dibuat dari templat Layanan Pekerja menentukan SDK Pekerja dalam file proyeknya:
<Project Sdk="Microsoft.NET.Sdk.Worker">
Untuk menggunakan templat sebagai dasar untuk aplikasi layanan yang dihosting:
- Buat proyek baru.
- Pilih Layanan Pekerja. Pilih Selanjutnya.
- Berikan nama proyek di bidang Nama proyek atau terima nama proyek default. Pilih Selanjutnya.
- Dalam dialog Informasi tambahan, Pilih Kerangka Kerja. Pilih Buat.
Package
Aplikasi berdasarkan templat Layanan Pekerja menggunakan Microsoft.NET.Sdk.Worker SDK dan memiliki referensi paket eksplisit ke paket Microsoft.Extensions.Hosting . Misalnya, lihat file proyek aplikasi sampel (BackgroundTasksSample.csproj).
Untuk aplikasi web yang menggunakan Microsoft.NET.Sdk.Web SDK, paket Microsoft.Extensions.Hosting dirujuk secara implisit dari kerangka kerja bersama. Referensi paket eksplisit dalam file proyek aplikasi tidak diperlukan.
Antarmuka IHostedService
Antarmuka IHostedService menentukan dua metode untuk objek yang dikelola oleh host:
StartAsync
StartAsync(CancellationToken) berisi logika untuk memulai tugas latar belakang.
StartAsync dipanggil sebelumnya:
- Alur pemrosesan permintaan aplikasi dikonfigurasi.
- Server dimulai dan IApplicationLifetime.ApplicationStarted dipicu.
StartAsync harus dibatasi untuk tugas yang berjalan singkat karena layanan yang dihosting dijalankan secara berurutan, dan tidak ada layanan lebih lanjut yang dimulai sampai StartAsync eksekusi hingga selesai.
StopAsync
-
StopAsync(CancellationToken) dipicu saat host melakukan pematian yang anggun.
StopAsyncberisi logika untuk mengakhiri tugas latar belakang. Menerapkan IDisposable dan menyelesaikan (destruktor) untuk membuang sumber daya yang tidak dikelola.
Token pembatalan memiliki batas waktu default 30 detik untuk menunjukkan bahwa proses matikan tidak boleh lagi anggun. Ketika pembatalan diminta pada token:
- Setiap operasi latar belakang yang tersisa yang dilakukan aplikasi harus dibatalkan.
- Metode apa pun yang dipanggil
StopAsyncharus segera kembali.
Namun, tugas tidak diabaikan setelah pembatalan diminta—penelepon menunggu semua tugas selesai.
Jika aplikasi dimatikan secara tiba-tiba (misalnya, proses aplikasi gagal), StopAsync mungkin tidak dipanggil. Oleh karena itu, metode apa pun yang disebut atau operasi yang dilakukan StopAsync mungkin tidak terjadi.
Untuk memperpanjang batas waktu mati 30 detik default, atur:
- ShutdownTimeout saat menggunakan Host Generik. Untuk informasi lebih lanjut, lihat .NET Generic Host di ASP.NET Core.
- Matikan pengaturan konfigurasi host batas waktu saat menggunakan Web Host. Untuk informasi selengkapnya, lihat ASP.NET Core Web Host.
Layanan yang dihosting diaktifkan sekali pada pengaktifan aplikasi dan dimatikan dengan lancar saat aplikasi dimatikan. Jika kesalahan dilemparkan selama eksekusi tugas latar belakang, Dispose harus dipanggil meskipun StopAsync tidak dipanggil.
Kelas dasar BackgroundService
BackgroundService adalah kelas dasar untuk menerapkan jangka panjang IHostedService.
ExecuteAsync(CancellationToken) dipanggil untuk menjalankan layanan latar belakang. Implementasi mengembalikan Task yang mewakili seluruh masa pakai layanan latar belakang. Tidak ada layanan lebih lanjut yang dimulai sampai ExecuteAsync menjadi asinkron, seperti dengan memanggil await. Hindari melakukan pekerjaan inisialisasi yang panjang dan memblokir di ExecuteAsync. Blok host di StopAsync(CancellationToken) menunggu ExecuteAsync untuk selesai.
Token pembatalan dipicu ketika IHostedService.StopAsync dipanggil. Implementasi ExecuteAsync Anda harus segera selesai ketika token pembatalan diaktifkan untuk mematikan layanan dengan baik. Jika tidak, layanan akan dimatikan dengan tidak sah pada waktu matikan. Untuk informasi selengkapnya, lihat bagian antarmuka IHostedService.
Untuk informasi selengkapnya, lihat kode sumber BackgroundService .
Tugas latar belakang berwaktu
Tugas latar belakang berwaktu menggunakan kelas System.Threading.Timer . Timer memicu metode tugas DoWork . Timer dinonaktifkan dan StopAsync dibuang ketika kontainer layanan dibuang pada 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 Tidak menunggu eksekusi DoWork sebelumnya selesai, sehingga pendekatan yang ditampilkan mungkin tidak cocok untuk setiap skenario.
Interlocked.Increment digunakan untuk menaikkan penghitung eksekusi sebagai operasi atomik, yang memastikan bahwa beberapa utas tidak diperbarui executionCount secara bersamaan.
Layanan ini terdaftar di IHostBuilder.ConfigureServices (Program.cs) dengan AddHostedService metode ekstensi:
services.AddHostedService<TimedHostedService>();
Menggunakan layanan tercakup dalam tugas latar belakang
Untuk menggunakan layanan terlingkup dalam BackgroundService, buat cakupan. Tidak ada cakupan yang dibuat untuk layanan yang dihosting secara default.
Layanan tugas latar belakang yang tercakup berisi logika tugas latar belakang. Dalam contoh berikut:
- Layanan ini asinkron. Metode mengembalikan
DoWorkTask. Untuk tujuan demonstrasi, penundaan sepuluh detik ditunggu dalam metode .DoWork - Disuntikkan ILogger ke dalam layanan.
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);
}
}
}
Layanan yang dihosting membuat cakupan untuk menyelesaikan layanan tugas latar belakang yang tercakup untuk memanggil metodenya DoWork .
DoWork
Taskmengembalikan , yang ditunggu dalam 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);
}
}
Layanan terdaftar di IHostBuilder.ConfigureServices (Program.cs). Layanan yang dihosting terdaftar dengan AddHostedService metode ekstensi:
services.AddHostedService<ConsumeScopedServiceHostedService>();
services.AddScoped<IScopedProcessingService, ScopedProcessingService>();
Tugas latar belakang yang diantrekan
Antrean tugas latar belakang didasarkan pada .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;
}
}
Dalam contoh berikut QueueHostedService :
- Metode mengembalikan
BackgroundProcessingTask, yang ditunggu dalamExecuteAsync. - Tugas latar belakang dalam antrean dihentikan antreannya dan dijalankan di
BackgroundProcessing. - Item kerja ditunggu sebelum layanan berhenti di
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);
}
}
MonitorLoop Layanan menangani tugas antrean untuk layanan yang dihosting setiap kali w kunci dipilih pada perangkat input:
-
IBackgroundTaskQueuedisuntikkan keMonitorLoopdalam layanan. -
IBackgroundTaskQueue.QueueBackgroundWorkItemdipanggil untuk mengantrekan item kerja. - Item kerja mensimulasikan tugas latar belakang yang berjalan lama:
- Tiga penundaan 5 detik dijalankan (
Task.Delay). - Pernyataan
try-catchmenjebak OperationCanceledException jika tugas dibatalkan.
- Tiga penundaan 5 detik dijalankan (
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);
}
}
}
Layanan terdaftar di IHostBuilder.ConfigureServices (Program.cs). Layanan yang dihosting terdaftar dengan AddHostedService metode ekstensi:
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 dimulai di Program.cs:
var monitorLoop = host.Services.GetRequiredService<MonitorLoop>();
monitorLoop.StartMonitorLoop();
Tugas latar belakang berwaktu asinkron
Kode berikut membuat tugas latar belakang berwaktu asinkron:
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);
}
}
Kompilasi AOT Asli
Templat Layanan Pekerja mendukung .NET native ahead-of-time (AOT) dengan --aot bendera:
- Buat proyek baru.
- Pilih Layanan Pekerja. Pilih Selanjutnya.
- Berikan nama proyek di bidang Nama proyek atau terima nama proyek default. Pilih Selanjutnya.
- Dalam dialog Informasi tambahan:
- Pilih Kerangka Kerja.
- Centang kotak centang Aktifkan penerbitan AOT Asli.
- Pilih Buat.
Opsi AOT menambahkan <PublishAot>true</PublishAot> ke file proyek:
<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>
Sumber daya tambahan
Di ASP.NET Core, tugas latar belakang dapat diimplementasikan sebagai layanan yang dihosting. Layanan yang dihosting adalah kelas dengan logika tugas latar belakang yang mengimplementasikan IHostedService antarmuka. Artikel ini menyediakan tiga contoh layanan yang dihosting:
- Tugas latar belakang yang berjalan pada timer.
- Layanan yang dihosting yang mengaktifkan layanan terlingkup. Layanan tercakup dapat menggunakan injeksi dependensi (DI).
- Tugas latar belakang antrean yang berjalan secara berurutan.
Templat Layanan Pekerja
Templat Layanan Pekerja Inti ASP.NET menyediakan titik awal untuk menulis aplikasi layanan yang berjalan lama. Aplikasi yang dibuat dari templat Layanan Pekerja menentukan SDK Pekerja dalam file proyeknya:
<Project Sdk="Microsoft.NET.Sdk.Worker">
Untuk menggunakan templat sebagai dasar untuk aplikasi layanan yang dihosting:
- Buat proyek baru.
- Pilih Layanan Pekerja. Pilih Selanjutnya.
- Berikan nama proyek di bidang Nama proyek atau terima nama proyek default. Pilih Selanjutnya.
- Dalam dialog Informasi tambahan, Pilih Kerangka Kerja. Pilih Buat.
Package
Aplikasi berdasarkan templat Layanan Pekerja menggunakan Microsoft.NET.Sdk.Worker SDK dan memiliki referensi paket eksplisit ke paket Microsoft.Extensions.Hosting . Misalnya, lihat file proyek aplikasi sampel (BackgroundTasksSample.csproj).
Untuk aplikasi web yang menggunakan Microsoft.NET.Sdk.Web SDK, paket Microsoft.Extensions.Hosting dirujuk secara implisit dari kerangka kerja bersama. Referensi paket eksplisit dalam file proyek aplikasi tidak diperlukan.
Antarmuka IHostedService
Antarmuka IHostedService menentukan dua metode untuk objek yang dikelola oleh host:
StartAsync
StartAsync(CancellationToken) berisi logika untuk memulai tugas latar belakang.
StartAsync dipanggil sebelumnya:
- Alur pemrosesan permintaan aplikasi dikonfigurasi.
- Server dimulai dan IApplicationLifetime.ApplicationStarted dipicu.
StartAsync harus dibatasi untuk tugas yang berjalan singkat karena layanan yang dihosting dijalankan secara berurutan, dan tidak ada layanan lebih lanjut yang dimulai sampai StartAsync eksekusi hingga selesai.
StopAsync
-
StopAsync(CancellationToken) dipicu saat host melakukan pematian yang anggun.
StopAsyncberisi logika untuk mengakhiri tugas latar belakang. Menerapkan IDisposable dan menyelesaikan (destruktor) untuk membuang sumber daya yang tidak dikelola.
Token pembatalan memiliki batas waktu default 30 detik untuk menunjukkan bahwa proses matikan tidak boleh lagi anggun. Ketika pembatalan diminta pada token:
- Setiap operasi latar belakang yang tersisa yang dilakukan aplikasi harus dibatalkan.
- Metode apa pun yang dipanggil
StopAsyncharus segera kembali.
Namun, tugas tidak diabaikan setelah pembatalan diminta—penelepon menunggu semua tugas selesai.
Jika aplikasi dimatikan secara tiba-tiba (misalnya, proses aplikasi gagal), StopAsync mungkin tidak dipanggil. Oleh karena itu, metode apa pun yang disebut atau operasi yang dilakukan StopAsync mungkin tidak terjadi.
Untuk memperpanjang batas waktu mati 30 detik default, atur:
- ShutdownTimeout saat menggunakan Host Generik. Untuk informasi lebih lanjut, lihat .NET Generic Host di ASP.NET Core.
- Matikan pengaturan konfigurasi host batas waktu saat menggunakan Web Host. Untuk informasi selengkapnya, lihat ASP.NET Core Web Host.
Layanan yang dihosting diaktifkan sekali pada pengaktifan aplikasi dan dimatikan dengan lancar saat aplikasi dimatikan. Jika kesalahan dilemparkan selama eksekusi tugas latar belakang, Dispose harus dipanggil meskipun StopAsync tidak dipanggil.
Kelas dasar BackgroundService
BackgroundService adalah kelas dasar untuk menerapkan jangka panjang IHostedService.
ExecuteAsync(CancellationToken) dipanggil untuk menjalankan layanan latar belakang. Implementasi mengembalikan Task yang mewakili seluruh masa pakai layanan latar belakang. Tidak ada layanan lebih lanjut yang dimulai sampai ExecuteAsync menjadi asinkron, seperti dengan memanggil await. Hindari melakukan pekerjaan inisialisasi yang panjang dan memblokir di ExecuteAsync. Blok host di StopAsync(CancellationToken) menunggu ExecuteAsync untuk selesai.
Token pembatalan dipicu ketika IHostedService.StopAsync dipanggil. Implementasi ExecuteAsync Anda harus segera selesai ketika token pembatalan diaktifkan untuk mematikan layanan dengan baik. Jika tidak, layanan akan dimatikan dengan tidak sah pada waktu matikan. Untuk informasi selengkapnya, lihat bagian antarmuka IHostedService.
Untuk informasi selengkapnya, lihat kode sumber BackgroundService .
Tugas latar belakang berwaktu
Tugas latar belakang berwaktu menggunakan kelas System.Threading.Timer . Timer memicu metode tugas DoWork . Timer dinonaktifkan dan StopAsync dibuang ketika kontainer layanan dibuang pada 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 Tidak menunggu eksekusi DoWork sebelumnya selesai, sehingga pendekatan yang ditampilkan mungkin tidak cocok untuk setiap skenario.
Interlocked.Increment digunakan untuk menaikkan penghitung eksekusi sebagai operasi atomik, yang memastikan bahwa beberapa utas tidak diperbarui executionCount secara bersamaan.
Layanan ini terdaftar di IHostBuilder.ConfigureServices (Program.cs) dengan AddHostedService metode ekstensi:
services.AddHostedService<TimedHostedService>();
Menggunakan layanan tercakup dalam tugas latar belakang
Untuk menggunakan layanan terlingkup dalam BackgroundService, buat cakupan. Tidak ada cakupan yang dibuat untuk layanan yang dihosting secara default.
Layanan tugas latar belakang yang tercakup berisi logika tugas latar belakang. Dalam contoh berikut:
- Layanan ini asinkron. Metode mengembalikan
DoWorkTask. Untuk tujuan demonstrasi, penundaan sepuluh detik ditunggu dalam metode .DoWork - Disuntikkan ILogger ke dalam layanan.
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);
}
}
}
Layanan yang dihosting membuat cakupan untuk menyelesaikan layanan tugas latar belakang yang tercakup untuk memanggil metodenya DoWork .
DoWork
Taskmengembalikan , yang ditunggu dalam 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);
}
}
Layanan terdaftar di IHostBuilder.ConfigureServices (Program.cs). Layanan yang dihosting terdaftar dengan AddHostedService metode ekstensi:
services.AddHostedService<ConsumeScopedServiceHostedService>();
services.AddScoped<IScopedProcessingService, ScopedProcessingService>();
Tugas latar belakang yang diantrekan
Antrean tugas latar belakang didasarkan pada .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;
}
}
Dalam contoh berikut QueueHostedService :
- Metode mengembalikan
BackgroundProcessingTask, yang ditunggu dalamExecuteAsync. - Tugas latar belakang dalam antrean dihentikan antreannya dan dijalankan di
BackgroundProcessing. - Item kerja ditunggu sebelum layanan berhenti di
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);
}
}
MonitorLoop Layanan menangani tugas antrean untuk layanan yang dihosting setiap kali w kunci dipilih pada perangkat input:
-
IBackgroundTaskQueuedisuntikkan keMonitorLoopdalam layanan. -
IBackgroundTaskQueue.QueueBackgroundWorkItemdipanggil untuk mengantrekan item kerja. - Item kerja mensimulasikan tugas latar belakang yang berjalan lama:
- Tiga penundaan 5 detik dijalankan (
Task.Delay). - Pernyataan
try-catchmenjebak OperationCanceledException jika tugas dibatalkan.
- Tiga penundaan 5 detik dijalankan (
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);
}
}
}
Layanan terdaftar di IHostBuilder.ConfigureServices (Program.cs). Layanan yang dihosting terdaftar dengan AddHostedService metode ekstensi:
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 dimulai di Program.cs:
var monitorLoop = host.Services.GetRequiredService<MonitorLoop>();
monitorLoop.StartMonitorLoop();
Tugas latar belakang berwaktu asinkron
Kode berikut membuat tugas latar belakang berwaktu asinkron:
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);
}
}
Sumber daya tambahan
Di ASP.NET Core, tugas latar belakang dapat diimplementasikan sebagai layanan yang dihosting. Layanan yang dihosting adalah kelas dengan logika tugas latar belakang yang mengimplementasikan IHostedService antarmuka. Artikel ini menyediakan tiga contoh layanan yang dihosting:
- Tugas latar belakang yang berjalan pada timer.
- Layanan yang dihosting yang mengaktifkan layanan terlingkup. Layanan tercakup dapat menggunakan injeksi dependensi (DI).
- Tugas latar belakang antrean yang berjalan secara berurutan.
Melihat atau mengunduh kode sampel (cara mengunduh)
Templat Layanan Pekerja
Templat Layanan Pekerja Inti ASP.NET menyediakan titik awal untuk menulis aplikasi layanan yang berjalan lama. Aplikasi yang dibuat dari templat Layanan Pekerja menentukan SDK Pekerja dalam file proyeknya:
<Project Sdk="Microsoft.NET.Sdk.Worker">
Untuk menggunakan templat sebagai dasar untuk aplikasi layanan yang dihosting:
- Buat proyek baru.
- Pilih Layanan Pekerja. Pilih Selanjutnya.
- Berikan nama proyek di bidang Nama proyek atau terima nama proyek default. Pilih Buat.
- Dalam dialog Buat layanan Pekerja baru, pilih Buat.
Package
Aplikasi berdasarkan templat Layanan Pekerja menggunakan Microsoft.NET.Sdk.Worker SDK dan memiliki referensi paket eksplisit ke paket Microsoft.Extensions.Hosting . Misalnya, lihat file proyek aplikasi sampel (BackgroundTasksSample.csproj).
Untuk aplikasi web yang menggunakan Microsoft.NET.Sdk.Web SDK, paket Microsoft.Extensions.Hosting dirujuk secara implisit dari kerangka kerja bersama. Referensi paket eksplisit dalam file proyek aplikasi tidak diperlukan.
Antarmuka IHostedService
Antarmuka IHostedService menentukan dua metode untuk objek yang dikelola oleh host:
StartAsync
StartAsync berisi logika untuk memulai tugas latar belakang.
StartAsync dipanggil sebelumnya:
- Alur pemrosesan permintaan aplikasi dikonfigurasi.
- Server dimulai dan IApplicationLifetime.ApplicationStarted dipicu.
Perilaku default dapat diubah sehingga layanan StartAsync yang dihosting berjalan setelah alur aplikasi dikonfigurasi dan ApplicationStarted dipanggil. Untuk mengubah perilaku default, tambahkan layanan yang dihosting (VideosWatcher dalam contoh berikut) setelah memanggil 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) dipicu saat host melakukan pematian yang anggun.
StopAsyncberisi logika untuk mengakhiri tugas latar belakang. Menerapkan IDisposable dan menyelesaikan (destruktor) untuk membuang sumber daya yang tidak dikelola.
Token pembatalan memiliki batas waktu lima detik default untuk menunjukkan bahwa proses matikan tidak boleh lagi anggun. Ketika pembatalan diminta pada token:
- Setiap operasi latar belakang yang tersisa yang dilakukan aplikasi harus dibatalkan.
- Metode apa pun yang dipanggil
StopAsyncharus segera kembali.
Namun, tugas tidak diabaikan setelah pembatalan diminta—penelepon menunggu semua tugas selesai.
Jika aplikasi dimatikan secara tiba-tiba (misalnya, proses aplikasi gagal), StopAsync mungkin tidak dipanggil. Oleh karena itu, metode apa pun yang disebut atau operasi yang dilakukan StopAsync mungkin tidak terjadi.
Untuk memperpanjang batas waktu mati lima detik default, atur:
- ShutdownTimeout saat menggunakan Host Generik. Untuk informasi lebih lanjut, lihat .NET Generic Host di ASP.NET Core.
- Matikan pengaturan konfigurasi host batas waktu saat menggunakan Web Host. Untuk informasi selengkapnya, lihat ASP.NET Core Web Host.
Layanan yang dihosting diaktifkan sekali pada pengaktifan aplikasi dan dimatikan dengan lancar saat aplikasi dimatikan. Jika kesalahan dilemparkan selama eksekusi tugas latar belakang, Dispose harus dipanggil meskipun StopAsync tidak dipanggil.
Kelas dasar BackgroundService
BackgroundService adalah kelas dasar untuk menerapkan jangka panjang IHostedService.
ExecuteAsync(CancellationToken) dipanggil untuk menjalankan layanan latar belakang. Implementasi mengembalikan Task yang mewakili seluruh masa pakai layanan latar belakang. Tidak ada layanan lebih lanjut yang dimulai sampai ExecuteAsync menjadi asinkron, seperti dengan memanggil await. Hindari melakukan pekerjaan inisialisasi yang panjang dan memblokir di ExecuteAsync. Blok host di StopAsync(CancellationToken) menunggu ExecuteAsync untuk selesai.
Token pembatalan dipicu ketika IHostedService.StopAsync dipanggil. Implementasi ExecuteAsync Anda harus segera selesai ketika token pembatalan diaktifkan untuk mematikan layanan dengan baik. Jika tidak, layanan akan dimatikan dengan tidak sah pada waktu matikan. Untuk informasi selengkapnya, lihat bagian antarmuka IHostedService.
StartAsync harus dibatasi untuk tugas yang berjalan singkat karena layanan yang dihosting dijalankan secara berurutan, dan tidak ada layanan lebih lanjut yang dimulai sampai StartAsync eksekusi hingga selesai. Tugas yang berjalan lama harus ditempatkan di ExecuteAsync. Untuk informasi selengkapnya, lihat sumber ke BackgroundService.
Tugas latar belakang berwaktu
Tugas latar belakang berwaktu menggunakan kelas System.Threading.Timer . Timer memicu metode tugas DoWork . Timer dinonaktifkan dan StopAsync dibuang ketika kontainer layanan dibuang pada 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 Tidak menunggu eksekusi DoWork sebelumnya selesai, sehingga pendekatan yang ditampilkan mungkin tidak cocok untuk setiap skenario.
Interlocked.Increment digunakan untuk menaikkan penghitung eksekusi sebagai operasi atomik, yang memastikan bahwa beberapa utas tidak diperbarui executionCount secara bersamaan.
Layanan ini terdaftar di IHostBuilder.ConfigureServices (Program.cs) dengan AddHostedService metode ekstensi:
services.AddHostedService<TimedHostedService>();
Menggunakan layanan tercakup dalam tugas latar belakang
Untuk menggunakan layanan terlingkup dalam BackgroundService, buat cakupan. Tidak ada cakupan yang dibuat untuk layanan yang dihosting secara default.
Layanan tugas latar belakang yang tercakup berisi logika tugas latar belakang. Dalam contoh berikut:
- Layanan ini asinkron. Metode mengembalikan
DoWorkTask. Untuk tujuan demonstrasi, penundaan sepuluh detik ditunggu dalam metode .DoWork - Disuntikkan ILogger ke dalam layanan.
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);
}
}
}
Layanan yang dihosting membuat cakupan untuk menyelesaikan layanan tugas latar belakang yang tercakup untuk memanggil metodenya DoWork .
DoWork
Taskmengembalikan , yang ditunggu dalam 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);
}
}
Layanan terdaftar di IHostBuilder.ConfigureServices (Program.cs). Layanan yang dihosting terdaftar dengan AddHostedService metode ekstensi:
services.AddHostedService<ConsumeScopedServiceHostedService>();
services.AddScoped<IScopedProcessingService, ScopedProcessingService>();
Tugas latar belakang yang diantrekan
Antrean tugas latar belakang didasarkan pada .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;
}
}
Dalam contoh berikut QueueHostedService :
- Metode mengembalikan
BackgroundProcessingTask, yang ditunggu dalamExecuteAsync. - Tugas latar belakang dalam antrean dihentikan antreannya dan dijalankan di
BackgroundProcessing. - Item kerja ditunggu sebelum layanan berhenti di
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);
}
}
MonitorLoop Layanan menangani tugas antrean untuk layanan yang dihosting setiap kali w kunci dipilih pada perangkat input:
-
IBackgroundTaskQueuedisuntikkan keMonitorLoopdalam layanan. -
IBackgroundTaskQueue.QueueBackgroundWorkItemdipanggil untuk mengantrekan item kerja. - Item kerja mensimulasikan tugas latar belakang yang berjalan lama:
- Tiga penundaan 5 detik dijalankan (
Task.Delay). - Pernyataan
try-catchmenjebak OperationCanceledException jika tugas dibatalkan.
- Tiga penundaan 5 detik dijalankan (
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);
}
}
}
Layanan terdaftar di IHostBuilder.ConfigureServices (Program.cs). Layanan yang dihosting terdaftar dengan AddHostedService metode ekstensi:
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 dimulai di Program.Main:
var monitorLoop = host.Services.GetRequiredService<MonitorLoop>();
monitorLoop.StartMonitorLoop();
Sumber daya tambahan
ASP.NET Core