При разработке конфигураций на платформе 1С:Предприятие с использованием Библиотеки Стандартных Подсистем (БСП) программисты часто сталкиваются с ситуацией, когда привычные методы платформы ведут себя иначе при использовании оберток БСП. Одним из таких камней преткновения является работа с фоновыми заданиями и временным хранилищем.
В этой статье мы подробно разберем проблему, с которой столкнулся автор темы: почему при прямом вызове ФоновыеЗадания.Выполнить данные временного хранилища доступны, а при использовании метода БСП ДлительныеОперации.ВыполнитьВФоне — нет. Мы проанализируем архитектурные причины этого поведения и рассмотрим правильные паттерны проектирования для передачи данных в фоновые процессы.
Рассмотрим ситуацию. У нас есть форма (например, созданная через подключаемые команды БСП), в которой пользователь что-то делает. Мы помещаем некоторые данные во временное хранилище, привязанное к этой форме (используя УникальныйИдентификатор формы). Затем мы хотим запустить фоновое задание, которое эти данные обработает.
Если мы используем стандартный метод платформы:
// Прямой вызов платформы
ФоновыеЗадания.Выполнить("МойМодуль.МояПроцедура", Параметры, , "Наименование");
В этом случае, если четвертым параметром (Ключ) или неявно передать идентификатор формы, платформа может обеспечить "магию" связывания сеансов, и данные хранилища могут быть прочитаны (хотя это тоже имеет свои ограничения).
Однако, стандарты разработки 1С рекомендуют использовать подсистему Длительные операции из БСП. Код превращается в следующий:
// Вызов через БСП
ПараметрыВыполнения = ДлительныеОперации.ПараметрыВыполненияВФоне(УникальныйИдентификатор);
ДлительныеОперации.ВыполнитьВФоне("МойМодуль.МояПроцедура", ПараметрыПроцедуры, ПараметрыВыполнения);
И именно здесь возникает проблема: внутри процедуры МояПроцедура, которая выполняется на сервере в фоне, попытка получить данные из временного хранилища по адресу, переданному с клиента, возвращает Неопределено. Разберемся, почему так происходит и как это исправить.
Чтобы понять корень проблемы, обратимся к документации и сообщению №10 из обсуждения. Синтаксис-помощник гласит:
Временное хранилище, сформированное в одном сеансе, недоступно из другого сеанса.
Фоновое задание — это всегда отдельный сеанс. Платформа 1С имеет механизм-исключение: передача данных из фонового задания в сеанс, инициировавший его, возможна (например, через помещение результата в хранилище). Обратная передача (чтение данных формы из фона) требует, чтобы фоновое задание "знало", к контексту какой формы оно относится.
Разработчики БСП используют процедуру-обертку. Функция ВыполнитьВФоне внутри себя вызывает ФоновыеЗадания.Выполнить, но делает это через свой общий модуль. Если явно не указать идентификатор формы в параметрах запуска БСП, связь разрывается.
Более того, разработчики БСП намеренно ограничивают такую практику. Рассмотрим почему.
Даже если технически заставить это работать, передача адреса временного хранилища, привязанного к форме, в фоновое задание несет серьезные риски:
Поэтому правильный подход заключается не в попытке "протащить" хранилище формы в фон, а в изменении архитектуры передачи данных.
Как справедливо замечено в сообщении №8, самый надежный способ — не использовать передачу адреса. Вместо этого нужно получить данные на сервере в контексте вызова (перед запуском) и передать их как значение.
Алгоритм действий:
Пример кода:
&НаСервере
Функция ЗапуститьОбработкуВФоне()
// 1. Получаем данные, которые нужно обработать
// Например, это таблица значений из формы или данные из хранилища
ДанныеДляОбработки = ПолучитьИзВременногоХранилища(АдресХранилищаНаФорме);
// Если данных нет, нет смысла запускать задание
Если ДанныеДляОбработки = Неопределено Тогда
Возврат Неопределено;
КонецЕсли;
// 2. Упаковываем данные в параметры процедуры
ПараметрыПроцедуры = Новый Структура;
ПараметрыПроцедуры.Вставить("ТаблицаДанных", ДанныеДляОбработки);
ПараметрыПроцедуры.Вставить("ДополнительныйПараметр", 123);
// 3. Настраиваем параметры запуска БСП
ПараметрыВыполнения = ДлительныеОперации.ПараметрыВыполненияВФоне(УникальныйИдентификатор);
ПараметрыВыполнения.НаименованиеФоновогоЗадания = "Обработка данных пользователя";
// 4. Запускаем
РезультатЗапуска = ДлительныеОперации.ВыполнитьВФоне(
"МойОбщийМодуль.ОбработатьДанные",
ПараметрыПроцедуры,
ПараметрыВыполнения
);
Возврат РезультатЗапуска;
КонецФункции
// Процедура фонового задания (в общем модуле)
Процедура ОбработатьДанные(Параметры, АдресРезультата) Экспорт
ТЗ = Параметры.ТаблицаДанных; // Работаем с копией данных, переданной в параметрах
// ... логика обработки
КонецПроцедуры
Этот метод полностью исключает зависимость от жизни формы. Даже если пользователь закроет 1С, фоновое задание на сервере доработает с переданной копией данных.
Иногда данных слишком много, чтобы передавать их через параметры (сериализация огромной Таблицы Значений в параметры может быть накладной). В этом случае можно использовать Временное Хранилище, но создать его нужно правильно.
Ошибка автора темы была в том, что хранилище было привязано к УникальныйИдентификатор формы. Чтобы данные были доступны в фоне и жили независимо от формы, нужно помещать их в хранилище с новым идентификатором.
Алгоритм действий:
УникальныйИдентификатор. Он не связан ни с одной формой.Пример реализации:
&НаСервере
Функция ЗапуститьТяжелуюОбработку()
// Данные большого объема
ОгромнаяТЗ = РеквизитФормыВЗначение("Объект").Товары.Выгрузить();
// ВАЖНО: Создаем новый идентификатор, не связанный с ЭтаФорма.УникальныйИдентификатор
ИДХранилища = Новый УникальныйИдентификатор;
// Помещаем данные. Теперь они будут жить до конца сеанса или до явного удаления
АдресДляФона = ПоместитьВоВременноеХранилище(ОгромнаяТЗ, ИДХранилища);
ПараметрыПроцедуры = Новый Структура;
ПараметрыПроцедуры.Вставить("АдресДанных", АдресДляФона);
ПараметрыВыполнения = ДлительныеОперации.ПараметрыВыполненияВФоне(УникальныйИдентификатор);
Результат = ДлительныеОперации.ВыполнитьВФоне(
"МойОбщийМодуль.ОбработкаТяжелыхДанных",
ПараметрыПроцедуры,
ПараметрыВыполнения
);
Возврат Результат;
КонецФункции
// В общем модуле
Процедура ОбработкаТяжелыхДанных(Параметры, АдресРезультата) Экспорт
// Получаем данные по адресу.
// Так как хранилище не привязано к форме, оно доступно (при условии жизни сеанса)
ТЗ = ПолучитьИзВременногоХранилища(Параметры.АдресДанных);
// ... обработка
КонецПроцедуры
Важное примечание: При использовании этого метода вы сами несете ответственность за очистку памяти. Если фоновое задание упадет или не удалит данные, они останутся в памяти сервера до конца сеанса пользователя.
Если вернуться к техническому вопросу "почему не передается контекст", ответ кроется в структуре ПараметрыВыполнения. В БСП есть возможность принудительно передать идентификатор формы в параметры запуска фонового задания, но это скрыто в деталях реализации.
Если в структуру ПараметрыВыполнения (третий параметр функции ВыполнитьВФоне) добавить свойство, которое БСП передаст в ключ фонового задания, можно добиться поведения, аналогичного платформенному.
Однако, мы настоятельно рекомендуем не использовать этот подход для чтения данных формы, так как он возвращает нас к проблеме "Жизненный цикл формы" (см. выше). Разработчики БСП не зря усложнили этот путь — это защита от ошибок проектирования.
ЭтаФорма.УникальныйИдентификатор). Это ненадежно.