При разработке высоконагруженных систем на платформе 1С:Предприятие часто возникает задача распараллеливания вычислений. Мы запускаем несколько фоновых заданий и в какой-то момент сталкиваемся с необходимостью обмениваться данными между ними "на лету" или иметь доступ к некой общей переменной (кэшу), не обращаясь при этом к основной базе данных (СУБД). Рассмотрим подробно, почему стандартная "общая переменная" невозможна и какими способами можно обойти это ограничение.
Для начала проанализируем ситуацию с точки зрения архитектуры платформы. Разберем важный момент: каждое фоновое задание в 1С — это отдельный сеанс. Это означает, что для каждого задания создается свое адресное пространство памяти. Переменные, созданные в одном сеансе, физически недоступны для другого сеанса. Попытка создать "общую переменную" в глобальном модуле не сработает, так как у каждого фонового задания будет своя копия этого модуля.
Многие разработчики надеются на использование кэша модулей с повторным использованием возвращаемых значений (ПовторноеИспользование). Однако этот кэш также инициализируется в рамках конкретного сеанса. Таким образом, данные, вычисленные в одном потоке, не попадут в кэш другого потока.
Одним из предложенных на форуме вариантов является передача МенеджерВременныхТаблиц в параметры фонового задания. Разберем по шагам, как это выглядит в коде:
// В основном сеансе
МенеджерВТ = Новый МенеджерВременныхТаблиц;
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = МенеджерВТ;
Запрос.Текст = "ВЫБРАТЬ 1 КАК Значение ПОМЕСТИТЬ ВТ_ОбщиеДанные";
Запрос.Выполнить();
ПараметрыЗадания = Новый Массив;
ПараметрыЗадания.Добавить(МенеджерВТ);
ФоновыеЗадания.Выполнить("МойМодуль.ВыполнитьОбработку", ПараметрыЗадания);
Важное уточнение: несмотря на то, что этот метод позволяет передать структуру таблиц, он не создает "общую разделяемую память". Более того, временные таблицы физически создаются в базе tempdb вашей СУБД (например, MS SQL Server). Если ваша цель — полностью исключить обращения к СУБД для повышения производительности, этот метод может не дать ожидаемого прироста, так как нагрузка на дисковую подсистему сервера БД все равно сохранится.
Если данные не меняются в процессе работы фоновых заданий, а должны быть просто доступны им для чтения, мы можем использовать ПоместитьВоВременноеХранилище(). Рассмотрим этот механизм:
Адрес (строку).Адрес в качестве параметра фоновому заданию.ПолучитьИзВременногоХранилища(Адрес).Однако здесь есть критическое ограничение: фоновое задание может прочитать данные из хранилища только в том случае, если оно было запущено из того же сеанса и хранилище привязано к этому сеансу (через УникальныйИдентификатор). В случае с независимыми фоновыми заданиями этот механизм требует очень аккуратной настройки идентификаторов сеансов владельцев.
Проанализируем ситуацию, когда нам нужно постоянно обмениваться данными. Вместо того чтобы пытаться связать два короткоживущих задания, мы можем создать третье "сервисное" фоновое задание. Оно будет выполнять роль брокера данных.
Выясним причину эффективности такого подхода: это задание постоянно находится в памяти, хранит в своих переменных нужные данные и может предоставлять их другим заданиям через механизмы оповещения или записи в специализированные таблицы-очереди. Для реализации паузы в таком задании, чтобы оно не нагружало процессор, рекомендуется использовать метод ОжидатьЗавершения(<Таймаут>), как упоминалось в обсуждении.
Если версия вашей платформы позволяет и настроен сервер взаимодействия, это самый близкий к "нативному" способ передачи сообщений между сеансами в реальном времени. Рассмотрим пример:
Одно фоновое задание может отправить сообщение в обсуждение, а другое — получить его через обработчик. Это происходит быстрее, чем транзакционная запись в таблицы СУБД. Мы используем объект СистемаВзаимодействия для регистрации сообщений, которые по сути и будут являться нашими "изменяемыми переменными".
Когда требуется настоящая "общая переменная" с доступом на скорости оперативной памяти, профессиональным стандартом является использование внешних хранилищ типа Redis или Memcached.
Разберем, как это работает в связке с 1С:
HTTPСоединение) или через внешние компоненты."Shared_Var", и любое другое задание мгновенно его считает.Это идеальный вариант для снижения нагрузки на СУБД, так как данные вообще не касаются жесткого диска в процессе обмена.
Если разработка внешних компонент или настройка Redis кажется избыточной, проанализируем вариант с использованием RAM-драйва. Мы создаем в операционной системе виртуальный диск, который физически расположен в ОЗУ.
Для 1С это будет выглядеть как обычный путь к файлу: "R:\shared_data.txt". Скорость чтения и записи на такой диск в десятки раз превышает скорость SSD. Это позволяет использовать обычную сериализацию в JSON или XML для обмена данными между заданиями, при этом нагрузка на физическую СУБД будет нулевой.
Подводя итог, можно сказать: "честной" общей переменной между фоновыми заданиями в рамках встроенного языка 1С не существует из-за архитектурной изоляции сеансов. Однако, в зависимости от сложности задачи, мы можем выбрать один из инструментов:
Выбор конкретного метода зависит от объема передаваемых данных и требований к скорости отклика системы.