При реализации массовой загрузки или обработки данных в 1С часто возникает необходимость распараллеливания задач. Использование нескольких фоновых заданий позволяет существенно ускорить процесс, однако порождает проблему мониторинга: как в реальном времени выводить пользователю общий процент выполнения или состояние каждого отдельного потока? Поскольку стандартные механизмы БСП (Библиотеки стандартных подсистем) часто ориентированы на отслеживание одного задания, разработчику приходится проектировать собственную архитектуру взаимодействия между потоками.
В этой статье мы подробно разберем несколько проверенных методов мониторинга многопоточных фоновых заданий, проанализируем их преимущества и недостатки, а также выясним, как избежать избыточной нагрузки на сервер при обновлении прогресса.
Разберем классический подход, при котором каждое фоновое задание записывает информацию о своем состоянии в специально созданный Регистр Сведений. Это позволяет хранить данные о прогрессе независимо от жизненного цикла сеанса.
Для реализации этого метода нам необходимо создать независимый регистр сведений, например, МониторингФоновыхЗаданий. В качестве измерений используем ИдентификаторЗадания (тип УникальныйИдентификатор), а в ресурсах — Процент, ТекстСостояния и КоличествоИтераций.
Рассмотрим важные нюансы этого подхода:
// Внутри фонового задания
ВсегоЗаписей = ДанныеДляОбработки.Количество();
Для Сч = 1 По ВсегоЗаписей Цикл
// Основная логика обработки
ОбработатьСтроку(ДанныеДляОбработки[Сч-1]);
// Обновляем прогресс раз в 100 итераций или раз в секунду
Если Сч % 100 = 0 Тогда
ЗаписатьПрогрессВРегистр(ИдентификаторПотока, (Сч / ВсегоЗаписей) * 100);
КонецЕсли;
КонецЦикла;
При использовании регистра важно учитывать блокировки. Выясним причину возможных проблем: если все потоки будут пытаться обновить одну и ту же запись, возникнет очередь. Поэтому крайне важно, чтобы каждое задание писало в свою уникальную строку, используя свой UUID.
Мало кто из разработчиков использует встроенную функцию платформы ФоновыеЗадания.УстановитьПрогресс(). Это гораздо более производительный способ, так как данные передаются через оперативную память кластера, не затрагивая таблицы базы данных.
Посмотрим, как это работает. Внутри фонового задания мы вызываем:
ФоновыеЗадания.УстановитьПрогресс(СтруктураДанных);
В структуру можно поместить любую информацию: процент, текущий статус или даже промежуточные результаты. В основном потоке (в форме) мы анализируем массив запущенных заданий:
Для Каждого Задание Из МассивЗаданий Цикл
ДанныеПрогресса = Задание.ПолучитьПрогресс();
Если ДанныеПрогресса <> Неопределено Тогда
// Обработка полученной структуры
ОбщийПроцент = ОбщийПроцент + ДанныеПрогресса.Процент / КоличествоПотоков;
КонецЕсли;
КонецЦикла;
Важное ограничение: Данный метод работает только пока объект ФоновоеЗадание существует в памяти. Если произойдет перезапуск рабочего процесса или задание аварийно завершится, данные могут быть потеряны, в отличие от хранения в регистре.
Проанализируем ситуацию, когда мы хотим использовать механизмы, заложенные в БСП. Библиотека стандартных подсистем активно использует метод ОбщегоНазначения.СообщитьПользователю() внутри фоновых заданий. Платформа накапливает эти сообщения в буфере фонового задания.
Разберем по шагам, как получить эти данные:
ТекстСообщенияПользователю, куда передает сериализованную строку с прогрессом (например, в формате JSON или просто через разделители).ПолучитьСообщенияПользователю(Истина).Истина очищает прочитанные сообщения, чтобы не получать их повторно.Этот метод считается достаточно «чистым» с точки зрения архитектуры 1С, так как он не создает лишних объектов в базе данных и использует штатный канал связи между серверными вызовами.
Если ваша конфигурация поддерживает Систему взаимодействия, мы можем реализовать наиболее современный способ мониторинга. Выясним преимущества: нам не нужен обработчик ожидания на клиенте, который постоянно «дергает» сервер.
Алгоритм действий следующий:
Этот способ требует наличия настроенного сервера системы взаимодействия, но обеспечивает минимальную задержку и максимальную интерактивность интерфейса.
Рассмотрим более сложную, но профессиональную схему. Вместо того чтобы просто запустить 20 заданий и ждать их завершения, мы создадим Очередь задач. Это может быть таблица в оперативной памяти или справочник.
Проанализируем логику работы:
Такой подход позволяет динамически регулировать нагрузку. Например, если сервер перегружен, мы можем не запускать новые потоки из очереди. Кроме того, это решает проблему «зависших» или слишком долгих потоков — если один поток столкнулся с тяжелым файлом, остальные продолжат разбирать очередь, и общий прогресс будет двигаться равномерно.
Часто возникает соблазн использовать ПоместитьВоВременноеХранилище. Однако стоит помнить: данные, помещенные в хранилище внутри фонового задания, по умолчанию становятся доступны родительскому сеансу только после завершения фонового задания. Для мониторинга *текущего* состояния в режиме реального времени этот механизм не подходит.
Выбор метода зависит от ваших задач:
УстановитьПрогресс.Помните, что точность прогресса до 1% обычно не требуется пользователю. Обновление состояния раз в 2-5 секунд является оптимальным балансом между информативностью и производительностью системы.