Как реализовать последовательный запуск фоновых заданий с разными параметрами в 1С?

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

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

Почему цикл на клиенте не эффективен

Рассмотрим типичную ошибку: программист пытается использовать цикл Для каждого на стороне клиента и внутри него вызывает фоновое задание, надеясь на использование стандартных механизмов ожидания БСП. Проблема заключается в том, что в управляемом приложении клиентский поток не может быть полностью заблокирован до завершения серверного процесса без нарушения логики работы интерфейса. Метод ДлительныеОперацииКлиент.ОжидатьЗавершения работает через механизм обратных вызовов (callback), а значит, код, следующий за вызовом этой процедуры в цикле, продолжит выполняться немедленно. В результате цикл «пролетает» до конца, пытаясь инициировать все задания сразу.

Проанализируем несколько стратегий, которые помогут обойти это ограничение и выстроить задачи в правильную очередь.

Способ 1: Передача коллекции параметров (Лучший вариант)

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

На стороне клиента мы подготавливаем данные:


ПараметрыЗапуска = Новый Массив;
Для Каждого Строка Из ТаблицаДанных Цикл
    ПараметрыЗапуска.Добавить(Строка.Значение);
КонецЦикла;

// Запускаем одно фоновое задание, передавая весь массив
ДлительныеОперации.ВыполнитьПроцедуруВФоне(УникальныйИдентификатор, "МойОбщийМодуль.ОбработатьСписокПараметров", ПараметрыЗапуска);

В общем модуле реализуем цикл уже внутри фонового процесса:


Процедура ОбработатьСписокПараметров(ПараметрыЗапуска) Экспорт
    Для Каждого ТекущийПараметр Из ПараметрыЗапуска Цикл
        // Выполняем полезную работу для каждого параметра последовательно
        ОбработатьОдинЭлемент(ТекущийПараметр);
    КонецЦикла;
КонецПроцедуры

Этот метод исключает накладные расходы на создание множества фоновых заданий и гарантирует, что данные будут обработаны строго друг за другом.

Способ 2: Использование серверного метода ожидания

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


Для Каждого Параметр Из МассивПараметров Цикл
    // Запускаем фоновое задание
    Задание = ФоновыеЗадания.Выполнить("МойОбщийМодуль.ВыполнитьЗадачу", Параметр);
    
    // Помещаем задание в массив для ожидания
    МассивЗаданий = Новый Массив;
    МассивЗаданий.Добавить(Задание);
    
    // Сервер ждет здесь до завершения (или таймаута)
    Попытка
        ФоновыеЗадания.ОжидатьЗавершения(МассивЗаданий);
        // Здесь можно проанализировать результат выполнения
    Исключение
        ЗаписатьЛогОшибки("Задание завершилось с ошибкой");
    КонецПопытки;
КонецЦикла;

Способ 3: Реализация цепочки через «Эстафету»

В ситуациях, когда мы не хотим занимать серверный поток ожиданием (например, цепочка очень длинная), можно применить метод «передачи эстафеты». Суть заключается в том, что каждое фоновое задание в самом конце своей работы инициирует запуск следующего экземпляра самого себя с новым набором параметров.

Разберем логику реализации:

  1. Подготавливаем массив всех параметров.
  2. Запускаем первое фоновое задание, передавая ему весь массив.
  3. Внутри задания извлекаем первый элемент массива для текущей обработки.
  4. После завершения обработки, если в массиве еще остались элементы, задание программно запускает новое фоновое задание с остатком массива.

Процедура ВыполнитьЗадачуЭстафетой(МассивПараметров) Экспорт
    Если МассивПараметров.Количество() = 0 Тогда
        Возврат;
    КонецЕсли;

    ТекущийПараметр = МассивПараметров[0];
    МассивПараметров.Удалить(0); // Убираем отработанный параметр

    // Выполняем основную работу
    ОбработатьДанные(ТекущийПараметр);

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

Способ 4: Клиентская рекурсия (Callback)

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

  1. Создаем на форме переменную (или реквизит), где будем хранить очередь параметров.
  2. Создаем процедуру ЗапуститьСледующееЗадание().
  3. В процедуре-обработчике завершения (которую вызывает ДлительныеОперацииКлиент) снова вызываем ЗапуститьСледующееЗадание().

Проанализируем пример кода на форме:


&НаКлиенте
Процедура НачатьОбработку()
    ЗаполнитьОчередьПараметров(); // Наполняем массив Объект.Очередь
    ЗапуститьСледующееЗадание();
КонецПроцедуры

&НаКлиенте
Процедура ЗапуститьСледующееЗадание()
    Если Объект.Очередь.Количество() = 0 Тогда
        Сообщить("Все задачи выполнены!");
        Возврат;
    КонецЕсли;

    ТекущийПараметр = Объект.Очередь[0].Значение;
    Объект.Очередь.Удалить(0);

    ПараметрыЗадания = Новый Структура("Параметр", ТекущийПараметр);
    Результат = ФоновоеВыполнениеНаСервере(ПараметрыЗадания);

    Оповещение = Новый ОписаниеОповещения("ЗавершениеЗадания", ЭтотОбъект);
    ДлительныеОперацииКлиент.ОжидатьЗавершения(Результат.ИдентификаторЗадания, Оповещение);
КонецПроцедуры

&НаКлиенте
Процедура ЗавершениеЗадания(Результат, ДополнительныеПараметры) Экспорт
    // Здесь обрабатываем результат текущего шага
    // И запускаем следующий
    ЗапуститьСледующееЗадание();
КонецПроцедуры

Использование уникальных ключей для синхронизации

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

Резюме: Выбор метода зависит от ваших условий. Если параметры известны заранее — передавайте их массивом в одно задание (Способ 1). Если нужно управлять процессом с клиента и видеть прогресс — используйте цепочку оповещений (Способ 4). Если же запуск происходит в пакетном режиме на сервере — используйте метод ожидания (Способ 2).

← На главную