Поделиться через


Реализуйте интерфейс IHostedService

Если вам нужен конечный контроль за пределами предоставленного BackgroundService, вы можете реализовать собственный IHostedService. Интерфейс IHostedService является основой для всех длительно работающих служб в .NET. Реализации пользовательских настроек регистрируются с использованием метода расширения AddHostedService<THostedService>(IServiceCollection).

В этом руководстве вы узнаете, как:

  • Реализуйте интерфейсы IHostedService и IAsyncDisposable.
  • Создайте службу на основе таймера.
  • Зарегистрируйте пользовательскую реализацию с использованием внедрения зависимостей и логирования.

Подсказка

Все исходные коды примеров "Workers in .NET" доступны в Samples Browser для скачивания. Дополнительные сведения см. в разделе Примеры кода: рабочие процессы в .NET.

Предпосылки

Создание нового проекта

Чтобы создать проект рабочей службы с помощью Visual Studio, выберите файл>нового>проекта.... В диалоговом окне "Создание проекта " найдите "Рабочая служба" и выберите шаблон "Рабочая служба". Если вы предпочитаете использовать интерфейс командной строки .NET, откройте любимый терминал в рабочем каталоге. Выполните команду dotnet new, и замените <Project.Name> на желаемое имя проекта.

dotnet new worker --name <Project.Name>

Для получения дополнительной информации о команде создания нового проекта рабочей службы в .NET CLI, см. dotnet new worker.

Подсказка

Если вы используете Visual Studio Code, вы можете запускать команды CLI .NET из интегрированного терминала. Дополнительные сведения см. в visual Studio Code: интегрированный терминал.

Создание службы таймера

Фоновая служба, основанная на таймере, использует класс System.Threading.Timer. Таймер активирует DoWork метод. Таймер отключается методом IHostLifetime.StopAsync(CancellationToken) и удаляется при удалении контейнера службы методом IAsyncDisposable.DisposeAsync():

Замените содержимое Worker шаблона следующим кодом C# и переименуйте файл в TimerService.cs:

namespace App.TimerHostedService;

public sealed class TimerService(ILogger<TimerService> logger) : IHostedService, IAsyncDisposable
{
    private readonly Task _completedTask = Task.CompletedTask;
    private int _executionCount = 0;
    private Timer? _timer;

    public Task StartAsync(CancellationToken stoppingToken)
    {
        logger.LogInformation("{Service} is running.", nameof(TimerHostedService));
        _timer = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.FromSeconds(5));

        return _completedTask;
    }

    private void DoWork(object? state)
    {
        int count = Interlocked.Increment(ref _executionCount);

        logger.LogInformation(
            "{Service} is working, execution count: {Count:#,0}",
            nameof(TimerHostedService),
            count);
    }

    public Task StopAsync(CancellationToken stoppingToken)
    {
        logger.LogInformation(
            "{Service} is stopping.", nameof(TimerHostedService));

        _timer?.Change(Timeout.Infinite, 0);

        return _completedTask;
    }

    public async ValueTask DisposeAsync()
    {
        if (_timer is IAsyncDisposable timer)
        {
            await timer.DisposeAsync();
        }

        _timer = null;
    }
}

Это важно

Это Worker был подкласс BackgroundService. Теперь TimerService реализует как интерфейсы IHostedService, так и IAsyncDisposable.

Имеет значение TimerServicesealedи каскадирует DisposeAsync вызов из своего _timer экземпляра. Дополнительные сведения о каскадном паттерне удаления см. в разделе "Реализация DisposeAsync метода.

При вызове StartAsync создается экземпляр таймера, и он начинает отсчет времени.

Подсказка

Timer не ждет завершения предыдущего метода DoWork, поэтому приведенный подход может подойти не для всех сценариев. Interlocked.Increment используется для увеличения счетчика выполнения как атомарная операция, что обеспечивает предотвращение одновременного обновления _executionCount несколькими потоками.

Замените существующее Program содержимое следующим кодом C#:

using App.TimerHostedService;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddHostedService<TimerService>();

IHost host = builder.Build();
host.Run();

Служба регистрируется в (Program.cs) с использованием метода расширения AddHostedService. Это тот же метод расширения, который используется при регистрации BackgroundService подклассов, так как они оба реализуют IHostedService интерфейс.

Дополнительные сведения о регистрации служб см. в статье внедрение зависимостей в .NET.

Проверка функциональности службы

Чтобы запустить приложение из Visual Studio, выберите F5 или выберите > пункт меню "Начать отладку". Если вы используете интерфейс командной строки .NET, выполните dotnet run команду из рабочего каталога:

dotnet run

Дополнительные сведения о команде выполнения .NET CLI см. в разделе dotnet run.

Позвольте приложению поработать некоторое время, чтобы сгенерировать несколько увеличений количества выполнения. Вы увидите выходные данные, аналогичные следующим:

info: App.TimerHostedService.TimerService[0]
      TimerHostedService is running.
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
      Content root path: .\timer-service
info: App.TimerHostedService.TimerService[0]
      TimerHostedService is working, execution count: 1
info: App.TimerHostedService.TimerService[0]
      TimerHostedService is working, execution count: 2
info: App.TimerHostedService.TimerService[0]
      TimerHostedService is working, execution count: 3
info: App.TimerHostedService.TimerService[0]
      TimerHostedService is working, execution count: 4
info: Microsoft.Hosting.Lifetime[0]
      Application is shutting down...
info: App.TimerHostedService.TimerService[0]
      TimerHostedService is stopping.

Если приложение запущено в Visual Studio, выберите "Остановить отладку>...". Кроме того, нажмите клавиши CTRL + C в окне консоли, чтобы сообщить об отмене.

См. также

Существует несколько связанных руководств, которые следует рассмотреть: