TGViewer
.NET Разработчик .NET Разработчик @netdeveloperdiary · 6.75K subscribers
Post #2705 2.95K
День 2241. #ЗаметкиНаПолях
Выполнение Очистки в BackgroundService
Есть что-то немного странное в шаблоне Worker Service в .NET.

Создадим новый проект Worker Service с помощью:
dotnet new worker -n MyWorker

Класс Worker использует BackgroundService в качестве базового класса. Worker имеет метод ExecuteAsync(…), который вызывается при запуске сервиса и принимает CancellationToken. В ExecuteAsync(…) есть цикл while, который проверяет, отменён ли CancellationToken. Если да, цикл завершается, и всё, что находится за пределами цикла, должно быть выполнено. В цикле есть вызов Task.Delay(1000, stopToken), который задерживается на 1 секунду и также принимает токен отмены.

Если Worker делает мало работы, задержка во время каждой итерации цикла велика по сравнению со временем, которое занимает фактическая работа. И отмена, скорее всего, произойдет во время периода задержки. Если CancellationToken отменяется во время задержки, Task.Delay(…) должен выбросить TaskCanceledException.

Но этого не происходит. Метод ExecuteAsync(…) прекращает работу, но исключение не выбрасывается. Код за пределами цикла while не выполняется. Это означает, что любой код очистки, который я хочу запустить при отмене BackgroundService, скорее всего, не будет выполнен, т.к. отмена обычно происходит во время задержки. Это странно.

Если запустить аналогичное консольное приложение, Task.Delay(…) выбросит TaskCanceledException, и мы получим исключение.
var cts = new CancellationTokenSource(3000);
var stopToken = cts.Token;

int count = 0;
while (!stopToken.IsCancellationRequested)
{
Console.WriteLine($"{++count} - {DateTime.Now:HH:mm:ss}");
await Task.Delay(1000, stopToken);
}

Console.WriteLine("Exiting");

Получим:
1 - 01:23:45
2 - 01:23:46
3 - 01:23:47
Unhandled exception. System.Threading.Tasks.TaskCanceledException: A task was canceled.
…

Но фоновый процесс не выбрасывает исключения!

Решение
Обернём Task.Delay(1000, stopsToken) в try…catch, перехватывая TaskCanceledException:
protected override async Task 
ExecuteAsync(CancellationToken stopToken)
{
while (!stopToken.IsCancellationRequested)
{
Console.WriteLine($"До: {DateTime.Now:HH:mm:ss}");
try
{
await Task.Delay(1000, stopToken);
}
catch (TaskCanceledException)
{
Console.WriteLine($"Отмена в Task.Delay.");
break;
}
Console.WriteLine($"После: {DateTime.Now:HH:mm:ss}");
}
Console.WriteLine("Очистка");
}

Если вы используете Task.Delay(1000) без токена отмены, код дождётся окончания задержки, прежде чем выходить из while, но любой код после выполнится.

Также можно попробовать:
await Task.Delay(1000, stopToken)
.ConfigureAwait(ConfigureAwaitOptions.SuppressThrowing);
// или
await Task.Delay(1000, stopToken)
.ContinueWith(t => { return t.Exception == default;});

Они тоже не прервут работу и выполнят код после задержки.

Источник: https://nodogmablog.bryanhogan.net/2025/02/doing-some-cleanup-in-a-canceled-background-service/
  • 👍 8
  • 👎 1
More from @netdeveloperdiary
  1. Oct 5, 2026День 2805. #ЧтоНовенького #NET11 Аргументы в Выражениях Коллекций в C#15 В C#15 реализован…
  2. Oct 4, 2026День 2804. #ВопросыНаСобеседовании Марк Прайс предложил свой набор из 60 вопросов (как тех…
  3. Oct 3, 2026Post #3360
  4. Oct 3, 2026День 2803. #Оффтоп Чем Заняться, Пока Работают Агенты? У VS Code Есть Ответ Сейчас большую…
  5. Oct 2, 2026День 2802. #Карьера #Юмор Секреты Программирования, Известные Только Легендам Ещё один пос…
  6. Oct 1, 2026Post #3357
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →