Реализация интерфейса IHostedService
Если требуется конечный элемент управления, не предусмотренный классом BackgroundService, можно реализовать собственный интерфейс IHostedService. Интерфейс IHostedService служит основой для всех длительно выполняющихся служб в .NET. Настраиваемые реализации регистрируются с помощью метода расширения AddHostedService<THostedService>(IServiceCollection).
В этом руководстве описано следующее:
- Реализуйте интерфейсы IHostedService и IAsyncDisposable.
- Создайте службу на основе таймера.
- Зарегистрируйте настраиваемую реализацию с помощью внедрения зависимостей и ведения журнала.
Совет
Весь пример исходного кода "Рабочие роли в .NET" доступен для скачивания в Обозревателе примеров. Дополнительные сведения см. в разделе Обзор примеров кода: рабочие роли в .NET.
Необходимые компоненты
- Пакет SDK для .NET 8.0 или более поздней версии
- Интегрированная среда разработки .NET (IDE)
- Можно использовать Visual Studio
Создание нового проекта
Чтобы создать новый проект службы рабочей роли с помощью Visual Studio, выберите Файл>Создать>Проект. В диалоговом окне Создание нового проекта выполните поиск по запросу "служба рабочей роли" и выберите шаблон "Служба рабочей роли". Если вы предпочитаете использовать .NET CLI, откройте используемый терминал в рабочем каталоге. Выполните команду dotnet new
и замените <Project.Name>
именем проекта.
dotnet new worker --name <Project.Name>
Дополнительные сведения о команде .NET CLI для создания проекта службы рабочей роли см. здесь.
Совет
Если вы используете Visual Studio Code, вы можете выполнять команды .NET CLI из интегрированного терминала. Дополнительные сведения см. в статье 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.
TimerService
находится в состоянии sealed
и выполняет каскадный вызов 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 CLI, выполните команду 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 в окне консоли, чтобы сообщить об отмене.
См. также
Рекомендуется ознакомиться с рядом учебников на смежные темы: