Планировщик задач на C# (.NET): Quartz.NET и System.Threading.Timer

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Планировщик задач на C# (.NET): от простого таймера до промышленного Quartz.NET

Планировщик задач (Task Scheduler) — неотъемлемая часть любого серьёзного приложения. Будь то периодическая очистка кэша, отправка email-рассылок или синхронизация данных с внешним API — без надёжного механизма выполнения фоновых операций не обойтись. В экосистеме .NET разработчику доступно несколько подходов: от встроенного System.Threading.Timer до мощного и гибкого фреймворка Quartz.NET. В этой статье мы разберём оба варианта, покажем примеры кода и поможем выбрать подходящий инструмент для вашего проекта.

1. Встроенный System.Threading.Timer

Самый простой и лёгкий способ запустить задачу по расписанию — использовать System.Threading.Timer. Он не требует установки сторонних пакетов и идеально подходит для простых сценариев: выполнение метода через фиксированные интервалы или однократная задержка.

Пример: выполнение задачи каждые 5 секунд

using System;
using System.Threading;

class Program
{
    static void Main()
    {
        // Создаём таймер, который запускает callback каждые 5 секунд
        Timer timer = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.FromSeconds(5));
        
        Console.WriteLine("Таймер запущен. Нажмите Enter для выхода.");
        Console.ReadLine();
        
        timer.Dispose();
    }

    static void DoWork(object state)
    {
        Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] Выполнение фоновой задачи...");
    }
}

Плюсы:

  • Встроен в .NET, не нужны дополнительные библиотеки.
  • Минимальное потребление ресурсов.
  • Простота использования.

Минусы:

  • Нет встроенной поддержки CRON-выражений.
  • Сложно управлять несколькими задачами с разными расписаниями.
  • Нет механизмов персистентности (задачи теряются при перезапуске).
  • Callback выполняется в потоке из пула, возможны проблемы с синхронизацией.

Для учебных проектов и простых утилит System.Threading.Timer — отличный выбор. Однако для production-приложений с десятками задач стоит присмотреться к более продвинутым решениям.

2. Quartz.NET — промышленный планировщик задач

Quartz.NET — это мощный, гибкий и зрелый фреймворк для планирования задач в .NET. Он вдохновлён популярным Java-аналогом Quartz и поддерживает:

  • CRON-выражения для гибкого расписания (каждый второй час, по будням в 9:00 и т.д.).
  • Персистентность задач (сохранение в БД).
  • Кластеризацию (несколько серверов могут выполнять задачи).
  • Пропущенные задачи (misfire handling).
  • Дочерние задачи и триггеры.

Установка Quartz.NET

Установите пакет через NuGet:

dotnet add package Quartz

Пример: простая задача с CRON-расписанием

using System;
using System.Threading.Tasks;
using Quartz;
using Quartz.Impl;

class Program
{
    static async Task Main()
    {
        // Создаём планировщик
        StdSchedulerFactory factory = new StdSchedulerFactory();
        IScheduler scheduler = await factory.GetScheduler();
        await scheduler.Start();

        // Определяем задачу (Job)
        IJobDetail job = JobBuilder.Create<HelloJob>()
            .WithIdentity("helloJob", "group1")
            .Build();

        // Создаём триггер: каждый день в 10:30 утра
        ITrigger trigger = TriggerBuilder.Create()
            .WithIdentity("trigger1", "group1")
            .WithCronSchedule("0 30 10 ? * * *")  // CRON: секунда, минута, час, день, месяц, день недели
            .Build();

        // Регистрируем задачу с триггером
        await scheduler.ScheduleJob(job, trigger);

        Console.WriteLine("Планировщик запущен. Нажмите Enter для выхода.");
        Console.ReadLine();

        await scheduler.Shutdown();
    }
}

// Класс, реализующий логику задачи
public class HelloJob : IJob
{
    public async Task Execute(IJobExecutionContext context)
    {
        await Console.Out.WriteLineAsync($"[{DateTime.Now:HH:mm:ss}] Привет от Quartz.NET!");
    }
}

CRON-выражение: разбор примера

В примере "0 30 10 ? * * *" означает:

Рекомендаций