ASP.NET Core içinde barındırılan hizmetlerle arka plan görevleri

, Jeow li Hua tarafından

ASP.NET Core, arka plan görevleri barındırılan hizmetler olarak uygulanabilir. Barındırılan hizmet, arabirimi uygulayan bir arka plan görevi mantığı olan bir sınıftır IHostedService . Bu makalede üç barındırılan hizmet örneği sunulmaktadır:

  • Bir Zamanlayıcı üzerinde çalışan arka plan görevi.
  • Kapsamlı bir hizmetietkinleştiren barındırılan hizmet. Kapsamlı hizmet bağımlılık ekleme (dı)kullanabilir.
  • Sıralı olarak çalışan sıraya alınmış arka plan görevleri.

Örnek kodu görüntüleme veya indirme (nasıl indirileceği)

Çalışan hizmeti şablonu

ASP.NET Core Worker hizmeti şablonu, uzun süre çalışan hizmet uygulamalarını yazmak için bir başlangıç noktası sağlar. Çalışan hizmeti şablonundan oluşturulan bir uygulama, çalışan SDK 'sını proje dosyasında belirtir:

<Project Sdk="Microsoft.NET.Sdk.Worker">

Şablonu bir barındırılan hizmetler uygulamasının temeli olarak kullanmak için:

  1. Yeni bir proje oluşturma.
  2. Çalışan hizmeti seçin. İleri’yi seçin.
  3. Project ad alanına bir proje adı girin veya varsayılan proje adını kabul edin. İleri’yi seçin.
  4. Ek bilgi iletişim kutusunda bir çerçeve seçin. Oluştur’u seçin.

Paket

Çalışan hizmeti şablonunu temel alan bir uygulama Microsoft.NET.Sdk.Worker SDK kullanır ve Microsoft. Extensions. Hosting paketine açık bir paket başvurusu içerir. Ö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 dolaylı olarak başvurulur. Uygulamanın proje dosyasındaki açık bir paket başvurusu gerekli değildir.

Ihostedservice arabirimi

IHostedServiceArabirim, konak tarafından yönetilen nesneler için iki yöntem tanımlar:

StartAsync

StartAsync arka plan görevinin başlatılacağı mantığı içerir. StartAsyncŞu kadar çağrılır:

Varsayılan davranış, barındırılan hizmetin StartAsync , uygulamanın işlem hattı yapılandırıldıktan ve çağrıldıktan sonra çalışması için değiştirilebilir ApplicationStarted . Varsayılan davranışı değiştirmek için, öğesini çağırdıktan sonra barındırılan hizmeti ( VideosWatcher Aşağıdaki örnekte) ekleyin CreateBuilder :

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddHostedService<VideosWatcher>();}

StopAsync

İptal belirtecinin, kapanma işleminin artık düzgün şekilde olmaması gerektiğini göstermek için varsayılan beş saniyelik bir zaman aşımı vardır. Belirteç üzerinde iptal istendiğinde:

  • Uygulamanın gerçekleştirdiği diğer arka plan işlemleri iptal edilmelidir.
  • İçinde çağrılan tüm yöntemler StopAsync hemen döndürmelidir.

Ancak, bir iptal işlemi istendikten sonra görevler terk edilmez — ve bu, ç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 olur), StopAsync çağrılmayabilir. Bu nedenle, veya üzerinde gerçekleştirilen işlemleri çağıran Yöntemler StopAsync gerçekleşmeyebilir.

Varsayılan beş saniyelik kapatılma zaman aşımını uzatmak için, şunu ayarlayın:

Barındırılan hizmet, uygulama başlangıcında bir kez etkinleştirilir ve uygulama kapatılırken düzgün şekilde kapanır. Arka plan görevinin yürütülmesi sırasında bir hata oluşursa, Dispose StopAsync çağrılmadıysa bile çağrılmalıdır.

BackgroundService temel sınıfı

BackgroundService , uzun süre çalışan uygulamaya yönelik bir temel sınıftır IHostedService .

Arka plan hizmetini çalıştırmak için ExecuteAsync (CancellationToken) çağırılır. Uygulama, Task arka plan hizmetinin tüm ömrünü temsil eden bir döndürür. ExecuteAsync, çağırarak gibi zaman uyumsuz halegelene kadar başka bir hizmet başlatılamaz await . İçinde başlatma işini uzun süre gerçekleştirmekten kaçının ExecuteAsync . StopAsync (CancellationToken) içindeki konak blokları tamamlanmasını bekliyor ExecuteAsync .

Ihostedservice. StopAsync çağrıldığında iptal belirteci tetiklenir. ExecuteAsyncHizmeti sorunsuz bir şekilde kapatmak için iptal belirteci tetiklendiğinde uygulamanızın uygulamanız hemen sona ermesini sağlar. Aksi takdirde, hizmet, kapanmanın zaman aşımından sonra kapanmadığını kaldırır. Daha fazla bilgi için ıhostedservice arabirimi bölümüne bakın.

StartAsync barındırılan hizmetler sıralı olarak çalıştırıldığı ve tamamlanana kadar başka hizmet başlatılana kadar, çalışan kısa görevlerle sınırlı olmalıdır StartAsync . Uzun süre çalışan görevler içine yerleştirilmelidir ExecuteAsync . Daha fazla bilgi için Backgroundservicekaynağına bakın.

Zamanlanmış arka plan görevleri

Zamanlanmış bir arka plan görevi, System. Threading. Timer sınıfından kullanımını sağlar. Zamanlayıcı, görevin DoWork metodunu tetikler. Zamanlayıcı devre dışı bırakılır StopAsync ve hizmet kapsayıcısı bırakıldığında bırakıldı 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 Önceki yürütmelerin bitmesini beklemez, bu DoWork nedenle gösterilen yaklaşım her senaryo için uygun olmayabilir. Interkilitlendi. Increment , yürütme sayacını bir atomik işlem olarak artırmak için kullanılır, bu da birden çok iş parçacığının eşzamanlı olarak güncelleştirilmesini sağlar executionCount .

Hizmet, IHostBuilder.ConfigureServices uzantı yöntemiyle (program. cs) kaydedilir AddHostedService :

services.AddHostedService<TimedHostedService>();

Bir arka plan görevinde kapsamlı bir hizmeti kullanma

Bir backgroundserviceiçinde kapsamlı hizmetler kullanmak için bir kapsam oluşturun. Barındırılan hizmet için varsayılan olarak kapsam oluşturulmaz.

Kapsamlı arka plan görev hizmeti, arka plan görevinin mantığını içerir. Aşağıdaki örnekte:

  • Hizmet zaman uyumsuz. DoWorkYöntemi bir döndürür Task . Tanıtım amacıyla, yöntemde on saniyelik bir gecikme beklenir DoWork .
  • ILoggerHizmete eklenen bir.
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 için kapsamlı arka plan görev hizmetini çözümlemek üzere bir kapsam oluşturur DoWork . DoWork şunu döndürür Task 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);
    }
}

Hizmetler ' de kaydedilir IHostBuilder.ConfigureServices (program. cs). Barındırılan hizmet, AddHostedService uzantı yöntemiyle kaydedilir:

services.AddHostedService<ConsumeScopedServiceHostedService>();
services.AddScoped<IScopedProcessingService, ScopedProcessingService>();

Sıraya alınan arka plan görevleri

Arka plan görev kuyruğu, .NET 4. x 'i temel alır 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;
    }
}

Aşağıdaki QueueHostedService örnekte:

  • BackgroundProcessingYöntemi Task , ' de beklemiş bir döndürür ExecuteAsync .
  • Kuyruktaki arka plan görevleri, içinde kuyruklanmış ve yürütülür BackgroundProcessing .
  • İş öğeleri, hizmetin durdurulmadan önce bekletildi 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);
    }
}

Bir MonitorLoop hizmet, giriş cihazında anahtar her seçildiğinde barındırılan hizmet için görevleri sıraya alır w :

  • , IBackgroundTaskQueue MonitorLoop Hizmete eklenir.
  • IBackgroundTaskQueue.QueueBackgroundWorkItem bir iş öğesini kuyruğa almak için çağrılır.
  • Çalışma öğesi uzun süre çalışan bir arka plan görevinin benzetimini yapar:
    • 3 5-ikinci gecikmeler yürütülür ( Task.Delay ).
    • try-catch OperationCanceledException Görev iptal edildiğinde bir ifade yakalar.
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 ' de kaydedilir IHostBuilder.ConfigureServices (program. cs). Barındırılan hizmet, AddHostedService uzantı yöntemiyle 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);
});

MonitorLoopprogram. cs' de başlatılır:

var monitorLoop = host.Services.GetRequiredService<MonitorLoop>();
monitorLoop.StartMonitorLoop();

Ek kaynaklar

ASP.NET Core, arka plan görevleri barındırılan hizmetler olarak uygulanabilir. Barındırılan hizmet, arabirimi uygulayan bir arka plan görevi mantığı olan bir sınıftır IHostedService . Bu makalede üç barındırılan hizmet örneği sunulmaktadır:

  • Bir Zamanlayıcı üzerinde çalışan arka plan görevi.
  • Kapsamlı bir hizmetietkinleştiren barındırılan hizmet. Kapsamlı hizmet bağımlılık ekleme (dı)kullanabilir.
  • Sıralı olarak çalışan sıraya alınmış arka plan görevleri.

Örnek kodu görüntüleme veya indirme (nasıl indirileceği)

Çalışan hizmeti şablonu

ASP.NET Core Worker hizmeti şablonu, uzun süre çalışan hizmet uygulamalarını yazmak için bir başlangıç noktası sağlar. Çalışan hizmeti şablonundan oluşturulan bir uygulama, çalışan SDK 'sını proje dosyasında belirtir:

<Project Sdk="Microsoft.NET.Sdk.Worker">

Şablonu bir barındırılan hizmetler uygulamasının temeli olarak kullanmak için:

  1. Yeni bir proje oluşturma.
  2. Çalışan hizmeti seçin. İleri’yi seçin.
  3. Proje adı alanında bir proje adı girin veya varsayılan proje adını kabul edin. Oluştur’u seçin.
  4. Yeni çalışan hizmeti oluştur Iletişim kutusunda Oluştur' u seçin.

Paket

Çalışan hizmeti şablonunu temel alan bir uygulama Microsoft.NET.Sdk.Worker SDK kullanır ve Microsoft. Extensions. Hosting paketine açık bir paket başvurusu içerir. Ö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 dolaylı olarak başvurulur. Uygulamanın proje dosyasındaki açık bir paket başvurusu gerekli değildir.

Ihostedservice arabirimi

IHostedServiceArabirim, konak tarafından yönetilen nesneler için iki yöntem tanımlar:

StartAsync

StartAsync arka plan görevinin başlatılacağı mantığı içerir. StartAsyncŞu kadar çağrılır:

Varsayılan davranış, barındırılan hizmetin StartAsync , uygulamanın işlem hattı yapılandırıldıktan ve çağrıldıktan sonra çalışması için değiştirilebilir ApplicationStarted . Varsayılan davranışı değiştirmek için, öğesini çağırdıktan sonra barındırılan hizmeti ( VideosWatcher Aşağıdaki örnekte) ekleyin 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

İptal belirtecinin, kapanma işleminin artık düzgün şekilde olmaması gerektiğini göstermek için varsayılan beş saniyelik bir zaman aşımı vardır. Belirteç üzerinde iptal istendiğinde:

  • Uygulamanın gerçekleştirdiği diğer arka plan işlemleri iptal edilmelidir.
  • İçinde çağrılan tüm yöntemler StopAsync hemen döndürmelidir.

Ancak, bir iptal işlemi istendikten sonra görevler terk edilmez — ve bu, ç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 olur), StopAsync çağrılmayabilir. Bu nedenle, veya üzerinde gerçekleştirilen işlemleri çağıran Yöntemler StopAsync gerçekleşmeyebilir.

Varsayılan beş saniyelik kapatılma zaman aşımını uzatmak için, şunu ayarlayın:

Barındırılan hizmet, uygulama başlangıcında bir kez etkinleştirilir ve uygulama kapatılırken düzgün şekilde kapanır. Arka plan görevinin yürütülmesi sırasında bir hata oluşursa, Dispose StopAsync çağrılmadıysa bile çağrılmalıdır.

BackgroundService temel sınıfı

BackgroundService , uzun süre çalışan uygulamaya yönelik bir temel sınıftır IHostedService .

Arka plan hizmetini çalıştırmak için ExecuteAsync (CancellationToken) çağırılır. Uygulama, Task arka plan hizmetinin tüm ömrünü temsil eden bir döndürür. ExecuteAsync, çağırarak gibi zaman uyumsuz halegelene kadar başka bir hizmet başlatılamaz await . İçinde başlatma işini uzun süre gerçekleştirmekten kaçının ExecuteAsync . StopAsync (CancellationToken) içindeki konak blokları tamamlanmasını bekliyor ExecuteAsync .

Ihostedservice. StopAsync çağrıldığında iptal belirteci tetiklenir. ExecuteAsyncHizmeti sorunsuz bir şekilde kapatmak için iptal belirteci tetiklendiğinde uygulamanızın uygulamanız hemen sona ermesini sağlar. Aksi takdirde, hizmet, kapanmanın zaman aşımından sonra kapanmadığını kaldırır. Daha fazla bilgi için ıhostedservice arabirimi bölümüne bakın.

StartAsync barındırılan hizmetler sıralı olarak çalıştırıldığı ve tamamlanana kadar başka hizmet başlatılana kadar, çalışan kısa görevlerle sınırlı olmalıdır StartAsync . Uzun süre çalışan görevler içine yerleştirilmelidir ExecuteAsync . Daha fazla bilgi için Backgroundservicekaynağına bakın.

Zamanlanmış arka plan görevleri

Zamanlanmış bir arka plan görevi, System. Threading. Timer sınıfından kullanımını sağlar. Zamanlayıcı, görevin DoWork metodunu tetikler. Zamanlayıcı devre dışı bırakılır StopAsync ve hizmet kapsayıcısı bırakıldığında bırakıldı 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 Önceki yürütmelerin bitmesini beklemez, bu DoWork nedenle gösterilen yaklaşım her senaryo için uygun olmayabilir. Interkilitlendi. Increment , yürütme sayacını bir atomik işlem olarak artırmak için kullanılır, bu da birden çok iş parçacığının eşzamanlı olarak güncelleştirilmesini sağlar executionCount .

Hizmet, IHostBuilder.ConfigureServices uzantı yöntemiyle (program. cs) kaydedilir AddHostedService :

services.AddHostedService<TimedHostedService>();

Bir arka plan görevinde kapsamlı bir hizmeti kullanma

Bir backgroundserviceiçinde kapsamlı hizmetler kullanmak için bir kapsam oluşturun. Barındırılan hizmet için varsayılan olarak kapsam oluşturulmaz.

Kapsamlı arka plan görev hizmeti, arka plan görevinin mantığını içerir. Aşağıdaki örnekte:

  • Hizmet zaman uyumsuz. DoWorkYöntemi bir döndürür Task . Tanıtım amacıyla, yöntemde on saniyelik bir gecikme beklenir DoWork .
  • ILoggerHizmete eklenen bir.
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 için kapsamlı arka plan görev hizmetini çözümlemek üzere bir kapsam oluşturur DoWork . DoWork şunu döndürür Task 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);
    }
}

Hizmetler ' de kaydedilir IHostBuilder.ConfigureServices (program. cs). Barındırılan hizmet, AddHostedService uzantı yöntemiyle kaydedilir:

services.AddHostedService<ConsumeScopedServiceHostedService>();
services.AddScoped<IScopedProcessingService, ScopedProcessingService>();

Sıraya alınan arka plan görevleri

Arka plan görev kuyruğu, .NET 4. x 'i temel alır 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;
    }
}

Aşağıdaki QueueHostedService örnekte:

  • BackgroundProcessingYöntemi Task , ' de beklemiş bir döndürür ExecuteAsync .
  • Kuyruktaki arka plan görevleri, içinde kuyruklanmış ve yürütülür BackgroundProcessing .
  • İş öğeleri, hizmetin durdurulmadan önce bekletildi 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);
    }
}

Bir MonitorLoop hizmet, giriş cihazında anahtar her seçildiğinde barındırılan hizmet için görevleri sıraya alır w :

  • , IBackgroundTaskQueue MonitorLoop Hizmete eklenir.
  • IBackgroundTaskQueue.QueueBackgroundWorkItem bir iş öğesini kuyruğa almak için çağrılır.
  • Çalışma öğesi uzun süre çalışan bir arka plan görevinin benzetimini yapar:
    • 3 5-ikinci gecikmeler yürütülür ( Task.Delay ).
    • try-catch OperationCanceledException Görev iptal edildiğinde bir ifade yakalar.
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 ' de kaydedilir IHostBuilder.ConfigureServices (program. cs). Barındırılan hizmet, AddHostedService uzantı yöntemiyle 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 Başlangıç Program.Main :

var monitorLoop = host.Services.GetRequiredService<MonitorLoop>();
monitorLoop.StartMonitorLoop();

Ek kaynaklar