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

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

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

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

Метод 1. Использование промежуточного регистра сведений

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

Для реализации этого метода нам необходимо создать независимый регистр сведений, например, МониторингФоновыхЗаданий. В качестве измерений используем ИдентификаторЗадания (тип УникальныйИдентификатор), а в ресурсах — Процент, ТекстСостояния и КоличествоИтераций.

Рассмотрим важные нюансы этого подхода:

  1. Минимизация нагрузки на БД. Нельзя записывать прогресс на каждой итерации цикла. Если мы обрабатываем 100 000 записей, запись в регистр 100 000 раз создаст огромную нагрузку на СУБД. Проанализируем пример кода с пакетным обновлением:

// Внутри фонового задания
ВсегоЗаписей = ДанныеДляОбработки.Количество();
Для Сч = 1 По ВсегоЗаписей Цикл
    // Основная логика обработки
    ОбработатьСтроку(ДанныеДляОбработки[Сч-1]);

    // Обновляем прогресс раз в 100 итераций или раз в секунду
    Если Сч % 100 = 0 Тогда
        ЗаписатьПрогрессВРегистр(ИдентификаторПотока, (Сч / ВсегоЗаписей) * 100);
    КонецЕсли;
КонецЦикла;

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

Метод 2. Штатный механизм платформы «УстановитьПрогресс»

Мало кто из разработчиков использует встроенную функцию платформы ФоновыеЗадания.УстановитьПрогресс(). Это гораздо более производительный способ, так как данные передаются через оперативную память кластера, не затрагивая таблицы базы данных.

Посмотрим, как это работает. Внутри фонового задания мы вызываем:

ФоновыеЗадания.УстановитьПрогресс(СтруктураДанных);

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


Для Каждого Задание Из МассивЗаданий Цикл
    ДанныеПрогресса = Задание.ПолучитьПрогресс();
    Если ДанныеПрогресса <> Неопределено Тогда
        // Обработка полученной структуры
        ОбщийПроцент = ОбщийПроцент + ДанныеПрогресса.Процент / КоличествоПотоков;
    КонецЕсли;
КонецЦикла;

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

Метод 3. Механизм сообщений пользователю (подход БСП)

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

Разберем по шагам, как получить эти данные:

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

Этот метод считается достаточно «чистым» с точки зрения архитектуры 1С, так как он не создает лишних объектов в базе данных и использует штатный канал связи между серверными вызовами.

Метод 4. Система взаимодействия (Push-уведомления)

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

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

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

Метод 5. Пул потоков и динамическое управление очередью

Рассмотрим более сложную, но профессиональную схему. Вместо того чтобы просто запустить 20 заданий и ждать их завершения, мы создадим Очередь задач. Это может быть таблица в оперативной памяти или справочник.

Проанализируем логику работы:

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

Такой подход позволяет динамически регулировать нагрузку. Например, если сервер перегружен, мы можем не запускать новые потоки из очереди. Кроме того, это решает проблему «зависших» или слишком долгих потоков — если один поток столкнулся с тяжелым файлом, остальные продолжат разбирать очередь, и общий прогресс будет двигаться равномерно.

Почему не стоит использовать Временное хранилище?

Часто возникает соблазн использовать ПоместитьВоВременноеХранилище. Однако стоит помнить: данные, помещенные в хранилище внутри фонового задания, по умолчанию становятся доступны родительскому сеансу только после завершения фонового задания. Для мониторинга *текущего* состояния в режиме реального времени этот механизм не подходит.

Итоговые рекомендации

Выбор метода зависит от ваших задач:

Помните, что точность прогресса до 1% обычно не требуется пользователю. Обновление состояния раз в 2-5 секунд является оптимальным балансом между информативностью и производительностью системы.

← На главную