Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Реализация интерфейса
Если требуется конечный элемент управления, не предусмотренный классом 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 в окне консоли, чтобы сообщить об отмене.
См. также
Рекомендуется ознакомиться с рядом учебников на смежные темы: