TGViewer
НеСерьезный шарпист НеСерьезный шарпист @serious_seesharp · 2.4K subscribers
Post #70 2.76K
В .NET 8 были выпущены интересные изменения в библиотеке Microsoft.Extensions.Hosting, на которые многие почему-то не обратили внимания.

Данная либа предоставляет возможности для создания фоновых задач в шаблоне ASP.NET приложения, а также для создания отдельных Worker-сервисов без привязки к веб-серверу (что, например, хорошо подходит для создания микросервисов, работающих с очередью брокера сообщений).

Приложение может содержать одну или несколько таких задач, реализующих IHostedService и регистрирующихся в контейнере внедрения зависимостей. Microsoft предоставляет готовую реализацию интерфейса IHostedService в виде базового абстрактного класса BackgroundService, предоставляет общую менимальную логику методов StartAsync() и StopAsync(), которая достаточно большинству фоновых задач. Чтобы использовать BackgroundService, необходимо унаследоваться от данного класса и реализуют абстрактный метод ExecuteAsync() с логикой фоновой задачи.
public class WorkerServiceOne : BackgroundService
{
private readonly ILogger<WorkerServiceOne> _logger;

public WorkerServiceOne(ILogger<WorkerServiceOne> logger)
{
_logger = logger;
}

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
_logger.LogInformation("Worker 1 running at: {time}", DateTimeOffset.Now);
await Task.Delay(1000, stoppingToken);
}
}
}


Для запуска служб создаем объект хоста и запускаем его. Вместе с запуском хоста будут запущены и все зарегистрированные службы.
IHost host = Host.CreateDefaultBuilder(args)
.ConfigureServices(services =>
{
services.AddHostedService<WorkerServiceOne>();
services.AddHostedService<WorkerServiceTwo>();
})
.Build();

host.Run();

Таким образом, каждая задача IHostedService, зарегистрированная в di-контейнере, запускается последовательно путем вызова метода StartAsync(). То есть для запуска следующий задачи выполняется ожидание завершения метод StartAsync() предыдущей задачи. Очевидно, что медленный запуск одной из служб тормозит развертывание всего приложения. А при остановке приложения выполняется такая же синхронная логика с StopAsync(). Однако для StopAsync() настраивается таймаут завершения и нужно учитывать время завершения каждого конкретного сервиса, а если завершение одной из служб будет занимать слишком много времени, весь процесс завершения приложения может завершаться аварийно.

Для решения этих проблем в .NET 8 была добавлена возможность "параллельного" запуска и завершения, регистрируемых служб. Для этого теперь существуют опции конфигурации ServicesStartConcurrently и ServicesStopConcurrently.
IHost host = Host.CreateDefaultBuilder(args)
.ConfigureServices(services =>
{
services.Configure<HostOptions>(options =>
{
options.ServicesStartConcurrently = true;
options.ServicesStopConcurrently = true;
});
services.AddHostedService<WorkerServiceOne>();
services.AddHostedService<WorkerServiceTwo>();
})
.Build();

host.Run();


Сейчас код реализации "параллельного" запуска служб хоста выглядит примерно следующим образом:
if (_options.ServicesStartConcurrently)
{
List<Task> tasks = new List<Task>();

foreach (IHostedService hostedService in _hostedServices)
{
tasks.Add(Task.Run(() => StartAndTryToExecuteAsync(hostedService, combinedCancellationToken), combinedCancellationToken));
}

Task groupedTasks = Task.WhenAll(tasks);
}


То есть задачи по-прежнему запускаются в том же порядке, в котором они регистрируются в di, но они не ожидаются на этом этапе. Задачи добавляются в список, и только после запуска каждой задачи WhenAll используется для ожидания завершения StartAsync для каждой службы. В этом режиме запуск займет столько же времени, сколько самый медленный вызов StartAsync, что позволяет приложению в целом запуститься быстрее. Аналогичная история и с StopAsync.
  • 👍 16
  • 🔥 5
  • ❤ 2
  • 🥰 1
More from @serious_seesharp
  1. Jul 11, 2026Знакомый создал бота @booster_tbot, для прохождения мок-собесов бесплатно, а вопросы основ…
  2. Jun 30, 2025Всем, привет! Мы сейчас начинаем отбор докладов на осеннюю конфу "Стачка" в Питере. Если у…
  3. Jun 28, 2025Столкнулся с тем, что, как выяснилось, не всем очевидно для чего нужен ValueTask и когда е…
  4. Jun 5, 2025Post #85
  5. May 24, 2025Давно хотел сделать розыгрыш, и вот, кажется, время настало 🥰 Не надо никуда ничего скиды…
  6. May 23, 2025👩‍💻 Когда в C# 12 появились primary constructors (первичные конструкторы класс/структур)…
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 →