ASP.NET Core'da barındırılan hizmetlerle arka plan görevleri
Tarafından Jeow Li Huan
Uyarı
ASP.NET Core'un bu sürümü artık desteklenmiyor. Daha fazla bilgi için bkz . .NET ve .NET Core Destek İlkesi. Geçerli sürüm için bu makalenin .NET 8 sürümüne bakın.
ASP.NET Core'da arka plan görevleri barındırılan hizmetler olarak uygulanabilir. Barındırılan hizmet, arabirimi uygulayan arka plan görev mantığına sahip bir sınıftır IHostedService . Bu makalede üç barındırılan hizmet örneği verilmiştir:
- Zamanlayıcı üzerinde çalışan arka plan görevi.
- Kapsamlı bir hizmeti etkinleştiren barındırılan hizmet. Kapsamı belirlenmiş hizmet bağımlılık ekleme (DI) kullanabilir.
- Sıralı olarak çalışan kuyruğa alınmış arka plan görevleri.
Çalışan Hizmeti şablonu
ASP.NET Temel Çalışan Hizmeti şablonu, uzun süre çalışan hizmet uygulamaları yazmak için bir başlangıç noktası sağlar. Çalışan Hizmeti şablonundan oluşturulan bir uygulama, proje dosyasında Çalışan SDK'sını belirtir:
<Project Sdk="Microsoft.NET.Sdk.Worker">
Şablonu barındırılan hizmetler uygulamasının temeli olarak kullanmak için:
- Yeni bir proje oluşturma.
- Çalışan Hizmeti'ne tıklayın. İleri'yi seçin.
- Proje adı alanına bir proje adı girin veya varsayılan proje adını kabul edin. İleri'yi seçin.
- Ek bilgi iletişim kutusunda Çerçeve Seçin. Oluştur'u belirleyin.
Paket
Çalışan Hizmeti şablonunu temel alan bir uygulama SDK'yi Microsoft.NET.Sdk.Worker
kullanır ve Microsoft.Extensions.Hosting paketine açık bir paket başvurusuna sahiptir. Örneğin, örnek uygulamanın proje dosyasına (BackgroundTasksSample.csproj
) bakın.
SDK kullanan web uygulamaları için Microsoft.NET.Sdk.Web
Microsoft.Extensions.Hosting paketine paylaşılan çerçeveden örtük olarak başvurulur. Uygulamanın proje dosyasında açık bir paket başvurusu gerekli değildir.
IHostedService arabirimi
Arabirim, IHostedService konak tarafından yönetilen nesneler için iki yöntem tanımlar:
StartAsync
StartAsync(CancellationToken), arka plan görevini başlatma mantığını içerir. StartAsync
daha önce çağrılır:
- Uygulamanın istek işleme işlem hattı yapılandırılır.
- Sunucu başlatılır ve IApplicationLifetime.ApplicationStarted tetiklendi .
StartAsync
barındırılan hizmetler sıralı olarak çalıştırıldığından ve tamamlanmaya kadar StartAsync
başka hizmet başlatılmadığından, kısa süre çalışan görevlerle sınırlı olmalıdır.
StopAsync
- Konak düzgün bir kapatma gerçekleştirdiğinde StopAsync(CancellationToken) tetiklenir.
StopAsync
arka plan görevini sonlandırma mantığını içerir. IDisposable Yönetilmeyen kaynakları atmak için uygulayıp sonlandırıcılar (yıkıcılar).
İptal belirtecinin kapatma işleminin artık düzgün olmaması gerektiğini belirtmek için varsayılan 30 saniyelik zaman aşımı vardır. Belirteçte iptal istendiğinde:
- Uygulamanın gerçekleştirdiği kalan arka plan işlemleri durdurulmalıdır.
- içinde
StopAsync
çağrılan tüm yöntemler hemen döndürülmelidir.
Ancak iptal istendikten sonra görevler terk edilmez; çağıran tüm görevlerin tamamlanmasını bekler.
Uygulama beklenmedik bir şekilde kapanırsa (örneğin, uygulamanın işlemi başarısız olursa) StopAsync
çağrılmayabilir. Bu nedenle, veya içinde gerçekleştirilen StopAsync
işlemler adlı herhangi bir yöntem gerçekleşmeyebilir.
Varsayılan 30 saniyelik kapatma zaman aşımını genişletmek için şunu ayarlayın:
- ShutdownTimeout Genel Ana Bilgisayar kullanılırken. Daha fazla bilgi için, bkz. ASP.NET Core'da .NET Genel Ana Bilgisayarı.
- Web Konağı kullanılırken kapatma zaman aşımı ana bilgisayar yapılandırma ayarı. Daha fazla bilgi için bkz . ASP.NET Core Web Host.
Barındırılan hizmet, uygulama başlangıcında bir kez etkinleştirilir ve uygulama kapatıldığında düzgün bir şekilde kapatılır. Arka plan görevi yürütme sırasında hata oluşursa, Dispose
çağrılmasa StopAsync
bile çağrılmalıdır.
BackgroundService temel sınıfı
BackgroundService , uzun süre çalışan IHostedServicebir uygulamak için temel bir sınıftır.
Arka plan hizmetini çalıştırmak için ExecuteAsync(CancellationToken) çağrılır. Uygulama, arka plan hizmetinin tüm ömrünü temsil eden bir Task döndürür. ExecuteAsync zaman uyumsuz hale gelene kadar, örneğin çağrısı await
yaparak başka hizmet başlatılmaz. içinde ExecuteAsync
uzun, başlatmayı engelleyen bir çalışma gerçekleştirmekten kaçının. StopAsync(CancellationToken) içindeki konak blokları tamamlanmasını bekliyorExecuteAsync
.
IHostedService.StopAsync çağrıldığında iptal belirteci tetikleniyor. uygulamasının ExecuteAsync
, hizmeti düzgün bir şekilde kapatmak için iptal belirteci tetiklendiğinde hemen bitmesi gerekir. Aksi takdirde, hizmet kapatma zaman aşımında düzgün bir şekilde kapatılır. Daha fazla bilgi için IHostedService arabirimi bölümüne bakın.
Daha fazla bilgi için bkz . BackgroundService kaynak kodu.
Zamanlanmış arka plan görevleri
Zamanlanmış arka plan görevi System.Threading.Timer sınıfını kullanır. Zamanlayıcı, görevin DoWork
yöntemini tetikler. Zamanlayıcı üzerinde devre dışı bırakılır StopAsync
ve hizmet kapsayıcısı üzerinde Dispose
atıldığında atılır:
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 önceki yürütmelerinin DoWork
tamamlanmasını beklemez, bu nedenle gösterilen yaklaşım her senaryo için uygun olmayabilir. Interlocked.Increment , birden çok iş parçacığının eşzamanlı olarak güncelleştirilmesini executionCount
engelleyen bir atomik işlem olarak yürütme sayacını artırmak için kullanılır.
Hizmet , uzantı yöntemiyle AddHostedService
(Program.cs
) içinde kaydedilir IHostBuilder.ConfigureServices
:
services.AddHostedService<TimedHostedService>();
Bir arka plan görevinde kapsamlı hizmet kullanma
Kapsamı belirlenmiş hizmetleri bir BackgroundService içinde kullanmak için bir kapsam oluşturun. Barındırılan bir hizmet için varsayılan olarak hiçbir kapsam oluşturulmaz.
Kapsamı belirlenmiş arka plan görev hizmeti, arka plan görevinin mantığını içerir. Aşağıdaki örnekte:
- Hizmet zaman uyumsuz.
DoWork
yöntemi birTask
döndürür. Gösterim amacıyla, yönteminde on saniyelik bir gecikme beklenmektedirDoWork
. - ILogger hizmetine eklenir.
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);
}
}
}
Barındırılan hizmet, yöntemini çağırmak DoWork
üzere kapsamlı arka plan görev hizmetini çözümlemek için bir kapsam oluşturur. DoWork
içinde beklenen ExecuteAsync
bir Task
döndürür:
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);
}
}
Hizmetler (Program.cs
içinde IHostBuilder.ConfigureServices
kayıtlıdır). Barındırılan hizmet uzantı yöntemiyle AddHostedService
kaydedilir:
services.AddHostedService<ConsumeScopedServiceHostedService>();
services.AddScoped<IScopedProcessingService, ScopedProcessingService>();
Kuyruğa alınan arka plan görevleri
Arka plan görev kuyruğu .NET 4.x'i QueueBackgroundWorkItemtemel alır:
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;
}
}
Aşağıdaki QueueHostedService
örnekte:
BackgroundProcessing
yöntemi içinde beklenenExecuteAsync
birTask
döndürür.- Kuyruktaki arka plan görevleri içinde sıralanır ve yürütülür
BackgroundProcessing
. - hizmet uygulamasında durdurulmadan
StopAsync
önce iş öğeleri beklenir.
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);
}
}
Bir MonitorLoop
hizmet, giriş cihazında anahtar seçildiğinde barındırılan w
hizmet için sıralama görevlerini işler:
IBackgroundTaskQueue
hizmetine eklenirMonitorLoop
.IBackgroundTaskQueue.QueueBackgroundWorkItem
bir iş öğesini sıralamak için çağrılır.- İş öğesi, uzun süre çalışan bir arka plan görevinin benzetimini gerçekleştirir:
- Üç 5 saniyelik gecikme yürütülür (
Task.Delay
). try-catch
Görev iptal edilirse bir deyim yakalanıyorOperationCanceledException.
- Üç 5 saniyelik gecikme yürütülür (
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);
}
}
}
Hizmetler (Program.cs
içinde IHostBuilder.ConfigureServices
kayıtlıdır). Barındırılan hizmet uzantı yöntemiyle AddHostedService
kaydedilir:
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
içinde Program.cs
başlatılır:
var monitorLoop = host.Services.GetRequiredService<MonitorLoop>();
monitorLoop.StartMonitorLoop();
Zaman uyumsuz zamanlanmış arka plan görevi
Aşağıdaki kod zaman uyumsuz zamanlanmış bir arka plan görevi oluşturur:
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.
DoWork();
using PeriodicTimer timer = new(TimeSpan.FromSeconds(1));
try
{
while (await timer.WaitForNextTickAsync(stoppingToken))
{
DoWork();
}
}
catch (OperationCanceledException)
{
_logger.LogInformation("Timed Hosted Service is stopping.");
}
}
// Could also be a async method, that can be awaited in ExecuteAsync above
private void DoWork()
{
int count = Interlocked.Increment(ref _executionCount);
_logger.LogInformation("Timed Hosted Service is working. Count: {Count}", count);
}
}
Yerel AOT
Çalışan Hizmeti şablonları, bayrağıyla --aot
.NET yerelini önceden (AOT) destekler:
- Yeni bir proje oluşturma.
- Çalışan Hizmeti'ne tıklayın. İleri'yi seçin.
- Proje adı alanına bir proje adı girin veya varsayılan proje adını kabul edin. İleri'yi seçin.
- Ek bilgi iletişim kutusunda:
- Bir Çerçeve seçin.
- Yerel AOT yayımlamayı etkinleştir onay kutusunu işaretleyin.
- Oluştur'u belirleyin.
AOT seçeneği proje dosyasına ekler <PublishAot>true</PublishAot>
:
<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>
Ek kaynaklar
ASP.NET Core'da arka plan görevleri barındırılan hizmetler olarak uygulanabilir. Barındırılan hizmet, arabirimi uygulayan arka plan görev mantığına sahip bir sınıftır IHostedService . Bu makalede üç barındırılan hizmet örneği verilmiştir:
- Zamanlayıcı üzerinde çalışan arka plan görevi.
- Kapsamlı bir hizmeti etkinleştiren barındırılan hizmet. Kapsamı belirlenmiş hizmet bağımlılık ekleme (DI) kullanabilir.
- Sıralı olarak çalışan kuyruğa alınmış arka plan görevleri.
Çalışan Hizmeti şablonu
ASP.NET Temel Çalışan Hizmeti şablonu, uzun süre çalışan hizmet uygulamaları yazmak için bir başlangıç noktası sağlar. Çalışan Hizmeti şablonundan oluşturulan bir uygulama, proje dosyasında Çalışan SDK'sını belirtir:
<Project Sdk="Microsoft.NET.Sdk.Worker">
Şablonu barındırılan hizmetler uygulamasının temeli olarak kullanmak için:
- Yeni bir proje oluşturma.
- Çalışan Hizmeti'ne tıklayın. İleri'yi seçin.
- Proje adı alanına bir proje adı girin veya varsayılan proje adını kabul edin. İleri'yi seçin.
- Ek bilgi iletişim kutusunda Çerçeve Seçin. Oluştur'u belirleyin.
Paket
Çalışan Hizmeti şablonunu temel alan bir uygulama SDK'yi Microsoft.NET.Sdk.Worker
kullanır ve Microsoft.Extensions.Hosting paketine açık bir paket başvurusuna sahiptir. Örneğin, örnek uygulamanın proje dosyasına (BackgroundTasksSample.csproj
) bakın.
SDK kullanan web uygulamaları için Microsoft.NET.Sdk.Web
Microsoft.Extensions.Hosting paketine paylaşılan çerçeveden örtük olarak başvurulur. Uygulamanın proje dosyasında açık bir paket başvurusu gerekli değildir.
IHostedService arabirimi
Arabirim, IHostedService konak tarafından yönetilen nesneler için iki yöntem tanımlar:
StartAsync
StartAsync(CancellationToken), arka plan görevini başlatma mantığını içerir. StartAsync
daha önce çağrılır:
- Uygulamanın istek işleme işlem hattı yapılandırılır.
- Sunucu başlatılır ve IApplicationLifetime.ApplicationStarted tetiklendi .
StartAsync
barındırılan hizmetler sıralı olarak çalıştırıldığından ve tamamlanmaya kadar StartAsync
başka hizmet başlatılmadığından, kısa süre çalışan görevlerle sınırlı olmalıdır.
StopAsync
- Konak düzgün bir kapatma gerçekleştirdiğinde StopAsync(CancellationToken) tetiklenir.
StopAsync
arka plan görevini sonlandırma mantığını içerir. IDisposable Yönetilmeyen kaynakları atmak için uygulayıp sonlandırıcılar (yıkıcılar).
İptal belirtecinin kapatma işleminin artık düzgün olmaması gerektiğini belirtmek için varsayılan 30 saniyelik zaman aşımı vardır. Belirteçte iptal istendiğinde:
- Uygulamanın gerçekleştirdiği kalan arka plan işlemleri durdurulmalıdır.
- içinde
StopAsync
çağrılan tüm yöntemler hemen döndürülmelidir.
Ancak iptal istendikten sonra görevler terk edilmez; çağıran tüm görevlerin tamamlanmasını bekler.
Uygulama beklenmedik bir şekilde kapanırsa (örneğin, uygulamanın işlemi başarısız olursa) StopAsync
çağrılmayabilir. Bu nedenle, veya içinde gerçekleştirilen StopAsync
işlemler adlı herhangi bir yöntem gerçekleşmeyebilir.
Varsayılan 30 saniyelik kapatma zaman aşımını genişletmek için şunu ayarlayın:
- ShutdownTimeout Genel Ana Bilgisayar kullanılırken. Daha fazla bilgi için, bkz. ASP.NET Core'da .NET Genel Ana Bilgisayarı.
- Web Konağı kullanılırken kapatma zaman aşımı ana bilgisayar yapılandırma ayarı. Daha fazla bilgi için bkz . ASP.NET Core Web Host.
Barındırılan hizmet, uygulama başlangıcında bir kez etkinleştirilir ve uygulama kapatıldığında düzgün bir şekilde kapatılır. Arka plan görevi yürütme sırasında hata oluşursa, Dispose
çağrılmasa StopAsync
bile çağrılmalıdır.
BackgroundService temel sınıfı
BackgroundService , uzun süre çalışan IHostedServicebir uygulamak için temel bir sınıftır.
Arka plan hizmetini çalıştırmak için ExecuteAsync(CancellationToken) çağrılır. Uygulama, arka plan hizmetinin tüm ömrünü temsil eden bir Task döndürür. ExecuteAsync zaman uyumsuz hale gelene kadar, örneğin çağrısı await
yaparak başka hizmet başlatılmaz. içinde ExecuteAsync
uzun, başlatmayı engelleyen bir çalışma gerçekleştirmekten kaçının. StopAsync(CancellationToken) içindeki konak blokları tamamlanmasını bekliyorExecuteAsync
.
IHostedService.StopAsync çağrıldığında iptal belirteci tetikleniyor. uygulamasının ExecuteAsync
, hizmeti düzgün bir şekilde kapatmak için iptal belirteci tetiklendiğinde hemen bitmesi gerekir. Aksi takdirde, hizmet kapatma zaman aşımında düzgün bir şekilde kapatılır. Daha fazla bilgi için IHostedService arabirimi bölümüne bakın.
Daha fazla bilgi için bkz . BackgroundService kaynak kodu.
Zamanlanmış arka plan görevleri
Zamanlanmış arka plan görevi System.Threading.Timer sınıfını kullanır. Zamanlayıcı, görevin DoWork
yöntemini tetikler. Zamanlayıcı üzerinde devre dışı bırakılır StopAsync
ve hizmet kapsayıcısı üzerinde Dispose
atıldığında atılır:
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 önceki yürütmelerinin DoWork
tamamlanmasını beklemez, bu nedenle gösterilen yaklaşım her senaryo için uygun olmayabilir. Interlocked.Increment , birden çok iş parçacığının eşzamanlı olarak güncelleştirilmesini executionCount
engelleyen bir atomik işlem olarak yürütme sayacını artırmak için kullanılır.
Hizmet , uzantı yöntemiyle AddHostedService
(Program.cs
) içinde kaydedilir IHostBuilder.ConfigureServices
:
services.AddHostedService<TimedHostedService>();
Bir arka plan görevinde kapsamlı hizmet kullanma
Kapsamı belirlenmiş hizmetleri bir BackgroundService içinde kullanmak için bir kapsam oluşturun. Barındırılan bir hizmet için varsayılan olarak hiçbir kapsam oluşturulmaz.
Kapsamı belirlenmiş arka plan görev hizmeti, arka plan görevinin mantığını içerir. Aşağıdaki örnekte:
- Hizmet zaman uyumsuz.
DoWork
yöntemi birTask
döndürür. Gösterim amacıyla, yönteminde on saniyelik bir gecikme beklenmektedirDoWork
. - ILogger hizmetine eklenir.
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);
}
}
}
Barındırılan hizmet, yöntemini çağırmak DoWork
üzere kapsamlı arka plan görev hizmetini çözümlemek için bir kapsam oluşturur. DoWork
içinde beklenen ExecuteAsync
bir Task
döndürür:
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);
}
}
Hizmetler (Program.cs
içinde IHostBuilder.ConfigureServices
kayıtlıdır). Barındırılan hizmet uzantı yöntemiyle AddHostedService
kaydedilir:
services.AddHostedService<ConsumeScopedServiceHostedService>();
services.AddScoped<IScopedProcessingService, ScopedProcessingService>();
Kuyruğa alınan arka plan görevleri
Arka plan görev kuyruğu .NET 4.x'i QueueBackgroundWorkItemtemel alır:
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;
}
}
Aşağıdaki QueueHostedService
örnekte:
BackgroundProcessing
yöntemi içinde beklenenExecuteAsync
birTask
döndürür.- Kuyruktaki arka plan görevleri içinde sıralanır ve yürütülür
BackgroundProcessing
. - hizmet uygulamasında durdurulmadan
StopAsync
önce iş öğeleri beklenir.
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);
}
}
Bir MonitorLoop
hizmet, giriş cihazında anahtar seçildiğinde barındırılan w
hizmet için sıralama görevlerini işler:
IBackgroundTaskQueue
hizmetine eklenirMonitorLoop
.IBackgroundTaskQueue.QueueBackgroundWorkItem
bir iş öğesini sıralamak için çağrılır.- İş öğesi, uzun süre çalışan bir arka plan görevinin benzetimini gerçekleştirir:
- Üç 5 saniyelik gecikme yürütülür (
Task.Delay
). try-catch
Görev iptal edilirse bir deyim yakalanıyorOperationCanceledException.
- Üç 5 saniyelik gecikme yürütülür (
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);
}
}
}
Hizmetler (Program.cs
içinde IHostBuilder.ConfigureServices
kayıtlıdır). Barındırılan hizmet uzantı yöntemiyle AddHostedService
kaydedilir:
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
içinde Program.cs
başlatılır:
var monitorLoop = host.Services.GetRequiredService<MonitorLoop>();
monitorLoop.StartMonitorLoop();
Zaman uyumsuz zamanlanmış arka plan görevi
Aşağıdaki kod zaman uyumsuz zamanlanmış bir arka plan görevi oluşturur:
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.
DoWork();
using PeriodicTimer timer = new(TimeSpan.FromSeconds(1));
try
{
while (await timer.WaitForNextTickAsync(stoppingToken))
{
DoWork();
}
}
catch (OperationCanceledException)
{
_logger.LogInformation("Timed Hosted Service is stopping.");
}
}
// Could also be a async method, that can be awaited in ExecuteAsync above
private void DoWork()
{
int count = Interlocked.Increment(ref _executionCount);
_logger.LogInformation("Timed Hosted Service is working. Count: {Count}", count);
}
}
Ek kaynaklar
ASP.NET Core'da arka plan görevleri barındırılan hizmetler olarak uygulanabilir. Barındırılan hizmet, arabirimi uygulayan arka plan görev mantığına sahip bir sınıftır IHostedService . Bu makalede üç barındırılan hizmet örneği verilmiştir:
- Zamanlayıcı üzerinde çalışan arka plan görevi.
- Kapsamlı bir hizmeti etkinleştiren barındırılan hizmet. Kapsamı belirlenmiş hizmet bağımlılık ekleme (DI) kullanabilir.
- Sıralı olarak çalışan kuyruğa alınmış arka plan görevleri.
Örnek kodu görüntüleme veya indirme (indirme)
Çalışan Hizmeti şablonu
ASP.NET Temel Çalışan Hizmeti şablonu, uzun süre çalışan hizmet uygulamaları yazmak için bir başlangıç noktası sağlar. Çalışan Hizmeti şablonundan oluşturulan bir uygulama, proje dosyasında Çalışan SDK'sını belirtir:
<Project Sdk="Microsoft.NET.Sdk.Worker">
Şablonu barındırılan hizmetler uygulamasının temeli olarak kullanmak için:
- Yeni bir proje oluşturma.
- Çalışan Hizmeti'ne tıklayın. İleri'yi seçin.
- Proje adı alanına bir proje adı girin veya varsayılan proje adını kabul edin. Oluştur'u belirleyin.
- Yeni Çalışan hizmeti oluştur iletişim kutusunda Oluştur'u seçin.
Paket
Çalışan Hizmeti şablonunu temel alan bir uygulama SDK'yi Microsoft.NET.Sdk.Worker
kullanır ve Microsoft.Extensions.Hosting paketine açık bir paket başvurusuna sahiptir. Örneğin, örnek uygulamanın proje dosyasına (BackgroundTasksSample.csproj
) bakın.
SDK kullanan web uygulamaları için Microsoft.NET.Sdk.Web
Microsoft.Extensions.Hosting paketine paylaşılan çerçeveden örtük olarak başvurulur. Uygulamanın proje dosyasında açık bir paket başvurusu gerekli değildir.
IHostedService arabirimi
Arabirim, IHostedService konak tarafından yönetilen nesneler için iki yöntem tanımlar:
StartAsync
StartAsync
arka plan görevini başlatma mantığını içerir. StartAsync
daha önce çağrılır:
- Uygulamanın istek işleme işlem hattı yapılandırılır.
- Sunucu başlatılır ve IApplicationLifetime.ApplicationStarted tetiklendi .
Uygulamanın işlem hattı yapılandırıldıktan ve ApplicationStarted
çağrıldıktan sonra barındırılan hizmetin StartAsync
çalışması için varsayılan davranış değiştirilebilir. Varsayılan davranışı değiştirmek için çağrısı yaptıktan sonra ConfigureWebHostDefaults
barındırılan hizmeti (VideosWatcher
aşağıdaki örnekte) ekleyin:
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
- Konak düzgün bir kapatma gerçekleştirdiğinde StopAsync(CancellationToken) tetiklenir.
StopAsync
arka plan görevini sonlandırma mantığını içerir. IDisposable Yönetilmeyen kaynakları atmak için uygulayıp sonlandırıcılar (yıkıcılar).
İptal belirtecinin kapatma işleminin artık düzgün olmaması gerektiğini belirtmek için varsayılan beş saniyelik bir zaman aşımı vardır. Belirteçte iptal istendiğinde:
- Uygulamanın gerçekleştirdiği kalan arka plan işlemleri durdurulmalıdır.
- içinde
StopAsync
çağrılan tüm yöntemler hemen döndürülmelidir.
Ancak iptal istendikten sonra görevler terk edilmez; çağıran tüm görevlerin tamamlanmasını bekler.
Uygulama beklenmedik bir şekilde kapanırsa (örneğin, uygulamanın işlemi başarısız olursa) StopAsync
çağrılmayabilir. Bu nedenle, veya içinde gerçekleştirilen StopAsync
işlemler adlı herhangi bir yöntem gerçekleşmeyebilir.
Varsayılan beş saniyelik kapatma zaman aşımını genişletmek için şunu ayarlayın:
- ShutdownTimeout Genel Ana Bilgisayar kullanılırken. Daha fazla bilgi için, bkz. ASP.NET Core'da .NET Genel Ana Bilgisayarı.
- Web Konağı kullanılırken kapatma zaman aşımı ana bilgisayar yapılandırma ayarı. Daha fazla bilgi için bkz . ASP.NET Core Web Host.
Barındırılan hizmet, uygulama başlangıcında bir kez etkinleştirilir ve uygulama kapatıldığında düzgün bir şekilde kapatılır. Arka plan görevi yürütme sırasında hata oluşursa, Dispose
çağrılmasa StopAsync
bile çağrılmalıdır.
BackgroundService temel sınıfı
BackgroundService , uzun süre çalışan IHostedServicebir uygulamak için temel bir sınıftır.
Arka plan hizmetini çalıştırmak için ExecuteAsync(CancellationToken) çağrılır. Uygulama, arka plan hizmetinin tüm ömrünü temsil eden bir Task döndürür. ExecuteAsync zaman uyumsuz hale gelene kadar, örneğin çağrısı await
yaparak başka hizmet başlatılmaz. içinde ExecuteAsync
uzun, başlatmayı engelleyen bir çalışma gerçekleştirmekten kaçının. StopAsync(CancellationToken) içindeki konak blokları tamamlanmasını bekliyorExecuteAsync
.
IHostedService.StopAsync çağrıldığında iptal belirteci tetikleniyor. uygulamasının ExecuteAsync
, hizmeti düzgün bir şekilde kapatmak için iptal belirteci tetiklendiğinde hemen bitmesi gerekir. Aksi takdirde, hizmet kapatma zaman aşımında düzgün bir şekilde kapatılır. Daha fazla bilgi için IHostedService arabirimi bölümüne bakın.
StartAsync
barındırılan hizmetler sıralı olarak çalıştırıldığından ve tamamlanmaya kadar StartAsync
başka hizmet başlatılmadığından, kısa süre çalışan görevlerle sınırlı olmalıdır. Uzun süre çalışan görevler içine ExecuteAsync
yerleştirilmelidir. Daha fazla bilgi için bkz. BackgroundService kaynağına.
Zamanlanmış arka plan görevleri
Zamanlanmış arka plan görevi System.Threading.Timer sınıfını kullanır. Zamanlayıcı, görevin DoWork
yöntemini tetikler. Zamanlayıcı üzerinde devre dışı bırakılır StopAsync
ve hizmet kapsayıcısı üzerinde Dispose
atıldığında atılır:
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 önceki yürütmelerinin DoWork
tamamlanmasını beklemez, bu nedenle gösterilen yaklaşım her senaryo için uygun olmayabilir. Interlocked.Increment , birden çok iş parçacığının eşzamanlı olarak güncelleştirilmesini executionCount
engelleyen bir atomik işlem olarak yürütme sayacını artırmak için kullanılır.
Hizmet , uzantı yöntemiyle AddHostedService
(Program.cs
) içinde kaydedilir IHostBuilder.ConfigureServices
:
services.AddHostedService<TimedHostedService>();
Bir arka plan görevinde kapsamlı hizmet kullanma
Kapsamı belirlenmiş hizmetleri bir BackgroundService içinde kullanmak için bir kapsam oluşturun. Barındırılan bir hizmet için varsayılan olarak hiçbir kapsam oluşturulmaz.
Kapsamı belirlenmiş arka plan görev hizmeti, arka plan görevinin mantığını içerir. Aşağıdaki örnekte:
- Hizmet zaman uyumsuz.
DoWork
yöntemi birTask
döndürür. Gösterim amacıyla, yönteminde on saniyelik bir gecikme beklenmektedirDoWork
. - ILogger hizmetine eklenir.
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);
}
}
}
Barındırılan hizmet, yöntemini çağırmak DoWork
üzere kapsamlı arka plan görev hizmetini çözümlemek için bir kapsam oluşturur. DoWork
içinde beklenen ExecuteAsync
bir Task
döndürür:
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);
}
}
Hizmetler (Program.cs
içinde IHostBuilder.ConfigureServices
kayıtlıdır). Barındırılan hizmet uzantı yöntemiyle AddHostedService
kaydedilir:
services.AddHostedService<ConsumeScopedServiceHostedService>();
services.AddScoped<IScopedProcessingService, ScopedProcessingService>();
Kuyruğa alınan arka plan görevleri
Arka plan görev kuyruğu .NET 4.x'i QueueBackgroundWorkItemtemel alır:
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;
}
}
Aşağıdaki QueueHostedService
örnekte:
BackgroundProcessing
yöntemi içinde beklenenExecuteAsync
birTask
döndürür.- Kuyruktaki arka plan görevleri içinde sıralanır ve yürütülür
BackgroundProcessing
. - hizmet uygulamasında durdurulmadan
StopAsync
önce iş öğeleri beklenir.
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);
}
}
Bir MonitorLoop
hizmet, giriş cihazında anahtar seçildiğinde barındırılan w
hizmet için sıralama görevlerini işler:
IBackgroundTaskQueue
hizmetine eklenirMonitorLoop
.IBackgroundTaskQueue.QueueBackgroundWorkItem
bir iş öğesini sıralamak için çağrılır.- İş öğesi, uzun süre çalışan bir arka plan görevinin benzetimini gerçekleştirir:
- Üç 5 saniyelik gecikme yürütülür (
Task.Delay
). try-catch
Görev iptal edilirse bir deyim yakalanıyorOperationCanceledException.
- Üç 5 saniyelik gecikme yürütülür (
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);
}
}
}
Hizmetler (Program.cs
içinde IHostBuilder.ConfigureServices
kayıtlıdır). Barındırılan hizmet uzantı yöntemiyle AddHostedService
kaydedilir:
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
içinde Program.Main
başlatılır:
var monitorLoop = host.Services.GetRequiredService<MonitorLoop>();
monitorLoop.StartMonitorLoop();
Ek kaynaklar
ASP.NET Core