Как организовать очередь фоновых заданий в 1С для последовательной обработки?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В процессе автоматизации часто возникает задача: по некоторому событию в системе необходимо запустить длительную фоновую обработку. Классический пример — распределение поступившего товара по заказам клиентов. Проблема в том, что такие события могут происходить очень часто. Если новое поступление товара произойдет до того, как завершится распределение предыдущего, запуск второго экземпляра обработки параллельно с первым может привести к некорректным данным и ошибкам.

Стоит задача — организовать выполнение таких обработок строго последовательно. Если одна обработка уже запущена, все новые вызовы должны становиться в очередь и запускаться только после завершения текущей. Разберем несколько подходов к решению этой задачи, от самых надежных до более простых в реализации.

Решение 1: Надежный подход через базу данных (Регистр сведений)

Это классический, наиболее надежный и управляемый способ организации очереди. Его суть заключается в том, чтобы не запускать фоновые задания напрямую из кода, а складывать информацию о необходимости их запуска в специальную таблицу в базе данных. А отдельный процесс — обработчик очереди — будет последовательно разбирать эти задачи.

Рассмотрим реализацию по шагам:

  1. Создание хранилища очереди. В конфигурации создается объект для хранения задач, например, регистр сведений ОчередьФоновыхЗаданий. Он должен быть непериодическим, с режимом записи "Независимый".

    Примерная структура измерений и ресурсов:

    • ИдентификаторЗадания (Измерение, тип УникальныйИдентификатор) — первичный ключ.
    • ИмяМетода (Измерение, тип Строка) — имя процедуры, которую нужно выполнить.
    • Состояние (Ресурс, тип Перечисление.СостоянияЗаданийОчереди) — со значениями "Новое", "ВОбработке", "Выполнено", "Ошибка".
    • ДатаСоздания (Ресурс, тип Дата) — для сортировки и выбора самой старой задачи.
    • Параметры (Ресурс, тип ХранилищеЗначения) — для передачи параметров в фоновое задание.
    • ИнформацияОбОшибке (Ресурс, тип Строка) — для записи текста ошибки, если она произойдет.
  2. Постановка задачи в очередь. В том месте, где раньше происходил прямой вызов фонового задания (например, при проведении документа поступления), теперь нужно добавлять запись в созданный регистр сведений. Вместо ФоновыеЗадания.Выполнить(...) пишем код, который создает новую запись в регистре ОчередьФоновыхЗаданий с состоянием "Новое" и сохраняет все необходимые параметры в ресурсе Параметры.

  3. Создание обработчика очереди. Создается регламентное задание (например, "РазборОчередиФоновыхЗаданий"), которое будет запускаться с небольшим интервалом, скажем, каждые 30-60 секунд. Это регламентное задание будет выполнять основную логику диспетчеризации.

  4. Логика обработчика.

    В процедуре, выполняемой регламентным заданием, реализуется следующий алгоритм:

    1. Проверить, не выполняется ли уже какая-то задача из очереди. Для этого можно сделать запрос к нашему регистру и поискать записи в состоянии "ВОбработке". Если такая запись есть — завершаем работу текущего сеанса регламентного задания. Это гарантирует, что одновременно будет работать только один обработчик.
    2. Если активных задач нет, выбираем из регистра одну самую старую задачу в состоянии "Новое".
    3. Важный момент: в одной транзакции необходимо изменить состояние этой задачи на "ВОбработке" и зафиксировать транзакцию. Это предотвратит ситуацию, когда два одновременно запущенных регламентных задания (например, из-за сбоя) попытаются обработать одну и ту же задачу.
    4. После успешной смены состояния запускаем фоновое задание с помощью ФоновыеЗадания.Выполнить(). В качестве параметров передаем ему идентификатор записи из регистра.
    5. Внутри самого фонового задания, после выполнения основной логики, оно должно обновить свою запись в регистре, установив состояние "Выполнено" или "Ошибка".

Преимущества этого подхода:

Недостатки:

Решение 2: "Облегченный" подход средствами платформы

Если нет строгих требований к сохранности очереди при перезапуске сервера, а важна простота реализации без изменения структуры конфигурации, можно воспользоваться встроенными возможностями платформы 1С. Этот метод основан на анализе уже запущенных фоновых заданий.

Алгоритм действий следующий:

В коде, который должен выполняться в фоновом задании, в самом его начале, необходимо добавить блок проверки и ожидания.

  1. С помощью метода ФоновыеЗадания.ПолучитьФоновыеЗадания() получаем коллекцию всех фоновых заданий на сервере.

  2. В цикле перебираем эту коллекцию и ищем задания, которые соответствуют нашей обработке. Искать можно по свойству ИмяМетода или, что надежнее, по свойству Ключ, которое можно задать при запуске задания. Также важно проверять состояние задания — нас интересуют только активные (СостояниеФоновогоЗадания.Активно).

  3. Необходимо исключить из поиска само текущее задание. Для этого можно сравнить свойство УникальныйИдентификатор.

  4. Если в результате поиска найдено другое активное задание нашей обработки, это означает, что предыдущий экземпляр еще не завершил работу. В этом случае текущее задание должно встать в "паузу" и дождаться его окончания. Для этого используется метод НайденноеЗадание.ОжидатьЗавершения().

  5. После того как ожидание завершится, текущее задание может продолжить свое выполнение.

Посмотрим на пример кода, который может быть в начале экспортной процедуры, вызываемой фоновым заданием:


Процедура ДлительнаяОбработкаНаСервере(Параметры) Экспорт

    // Получаем собственное фоновое задание, чтобы знать свой ID
    ТекущееЗадание = ФоновыеЗадания.ТекущееФоновоеЗадание();
    Если ТекущееЗадание = Неопределено Тогда
        // Это не фоновое задание, возможно, запуск в отладке.
        // Продолжаем выполнение или вызываем исключение.
    КонецЕсли;

    // Ищем другие активные экземпляры этого же задания
    Отбор = Новый Структура;
    Отбор.Вставить("Ключ", "РаспределениеТоваровПоЗаказам"); // Используем одинаковый ключ для всех заданий
    Отбор.Вставить("Состояние", СостояниеФоновогоЗадания.Активно);

    НайденныеЗадания = ФоновыеЗадания.ПолучитьФоновыеЗадания(Отбор);
    
    Для Каждого Задание Из НайденныеЗадания Цикл
        
        // Пропускаем самих себя в списке
        Если ТекущееЗадание <> Неопределено И Задание.УникальныйИдентификатор = ТекущееЗадание.УникальныйИдентификатор Тогда
            Продолжить;
        КонецЕсли;
        
        // Нашли работающий экземпляр, ждем его завершения
        Попытка
            Задание.ОжидатьЗавершения();
        Исключение
            // Обрабатываем ситуацию, если задание было прервано администратором
            ЗаписьЖурналаРегистрации("Ожидание фонового задания", УровеньЖурналаРегистрации.Ошибка, , , ИнформацияОбОшибке());
        КонецПопытки;
        
    КонецЦикла;

    //
    // ... Здесь начинается основная логика фоновой обработки ...
    //

КонецПроцедуры

Преимущества этого подхода:

Недостатки:

Решение 3: Варианты с саморегулирующейся очередью и блокировками

Можно скомбинировать подход с базой данных, но избавиться от регламентного задания. Логика здесь сложнее и требует аккуратного обращения с блокировками.

Этот метод позволяет минимизировать количество запусков фоновых заданий и задержки, связанные с интервалом регламентного задания, но он значительно сложнее в реализации и отладке из-за необходимости корректно управлять блокировками для избежания "гонки состояний" (race condition).

Заключение

Выбор способа организации очереди фоновых заданий зависит от требований конкретной задачи.

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

Для более простых сценариев, где нужно лишь предотвратить параллельное выполнение и потеря очереди при перезапуске сервера некритична, можно использовать "облегченный" метод с ПолучитьФоновыеЗадания() и ОжидатьЗавершения(). Он прост в реализации и не требует доработки конфигурации.

Также стоит помнить, что в типовых конфигурациях, разработанных на базе Библиотеки стандартных подсистем (БСП), уже могут существовать готовые механизмы очередей заданий (например, подсистема ОчередьЗаданий). Перед созданием собственного решения всегда полезно изучить имеющиеся в конфигурации стандартные инструменты.

← На главную