Что знаешь о планировщике (scheduler) в .NET?

«Что знаешь о планировщике (scheduler) в .NET?» — вопрос из категории Многопоточность, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

В контексте .NET под "шедулером" (scheduler) обычно понимают механизмы для планирования и выполнения асинхронных или отложенных задач. Вот ключевые компоненты:

1. TaskScheduler и SynchronizationContext Это базовые абстракции, определяющие где и как выполняется код задачи (Task).

  • ThreadPool TaskScheduler: По умолчанию задачи выполняются в пуле потоков.
  • SynchronizationContext: Позволяет перенаправить продолжение задачи в конкретный контекст (например, в UI-поток с помощью Dispatcher в WPF или Control.Invoke в WinForms).

2. Практические инструменты для планирования:

  • Task.Delay: Простой способ асинхронно приостановить выполнение на указанное время, не блокируя поток.

    await Task.Delay(TimeSpan.FromSeconds(5)); // Асинхронная задержка на 5 секунд
  • System.Threading.Timer: Низкоуровневый таймер для периодического выполнения callback-метода в потоке из пула.

    var timer = new Timer(state => Console.WriteLine("Tick"), 
                          null, 
                          dueTime: 0,          // Запустить сразу
                          period: 1000);       // Повторять каждую секунду
    // Важно: реализовать корректную остановку и dispose.
  • IHostedService / BackgroundService (ASP.NET Core): Стандартный способ создания долгоживущих фоновых служб.

    public class MyBackgroundService : BackgroundService
    {
        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                await DoWorkAsync();
                await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
            }
        }
    }
  • Сторонние библиотеки: Для сложного планирования (cron-выражения, persistent-хранилище) используют Hangfire или Quartz.NET.

Критически важные практики:

  • Всегда используйте CancellationToken для возможности корректной отмены.
  • В UI-приложениях используйте TaskScheduler.FromCurrentSynchronizationContext() или Dispatcher для обновления UI из фоновой задачи.
  • Для периодических задач в ASP.NET Core предпочтительнее BackgroundService, а не Timer, так как он интегрирован в жизненный цикл приложения.