Как вернуть результат работы фонового задания во временное хранилище в Обычных формах 1С

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

При переходе на многопоточную обработку данных разработчики часто сталкиваются со спецификой работы платформы 1С:Предприятие при взаимодействии клиентской и серверной частей. В Управляемых формах (УФ) механизм возврата результатов из фонового задания во временное хранилище реализован элегантно и просто за счет использования встроенного свойства ЭтаФорма.УникальныйИдентификатор. Однако в Обычных формах (ОФ) аналогичного и прямолинейного механизма «из коробки» не предусмотрено. Попытки использовать свойство формы КлючУникальности напрямую на клиенте часто приводят к ошибкам или потере данных.

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

---

В чем заключается главная проблема Обычных форм?

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

Для реализации этого механизма необходимо соблюдать строгую последовательность шагов:

  1. В родительском сеансе резервируется адрес во временном хранилище путем помещения туда пустого значения (Неопределено) с привязкой к уникальному идентификатору формы.
  2. Полученный адрес передается в фоновое задание в качестве одного из параметров запуска.
  3. Фоновое задание производит вычисления и помещает финальный результат (например, таблицу значений или структуру) по этому же адресу.
  4. Родительский сеанс считывает данные из хранилища по зарезервированному адресу после завершения работы фонового процесса.

В Управляемом приложении идентификатором выступает ЭтаФорма.УникальныйИдентификатор. В Обычном приложении (Толстый клиент) попытка сгенерировать идентификатор на клиенте через Новый УникальныйИдентификатор или использовать КлючУникальности формы приводит к тому, что временное хранилище инициализируется на клиенте, но не связывается с контекстом сервера приложений должным образом. В результате фоновое задание не может записать данные по этому адресу, либо родительский сеанс не может их прочитать.

---

Пошаговое решение: Перенос контекста на сервер

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

Шаг 1. Настройка общего модуля

Создайте общий модуль (например, ФоновыеПроцедурыСервер) и установите для него следующие свойства в палитре свойств:

Важно: Отсутствие клиентских флагов гарантирует, что компиляция функций работы с временным хранилищем будет происходить строго в адресном пространстве сервера 1С. Это исключает конфликты контекстов в Толстом клиенте.

Шаг 2. Разработка методов общего модуля

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


// Функция резервирует адрес во временном хранилище на сервере
Функция ПолучитьАдресРезультатаФоновогоЗадания(УникальныйИдентификатор) Экспорт
    Возврат ПоместитьВоВременноеХранилище(Неопределено, УникальныйИдентификатор);
КонецФункции

// Процедура, выполняемая в фоновом задании
Процедура ВыполнитьТяжелыеВычисления(АдресЗаписи) Экспорт
    // Имитируем расчет данных
    ТаблицаРезультат = Новый ТаблицаЗначений;
    ТаблицаРезультат.Колонки.Добавить("Код");
    ТаблицаРезультат.Колонки.Добавить("Наименование");
    
    Для Сч = 1 По 5 Цикл
        НоваяСтрока = ТаблицаРезультат.Добавить();
        НоваяСтрока.Код = Сч;
        НоваяСтрока.Наименование = "Товар № " + Строка(Сч);
    КонецЦикла;
    
    // Записываем результат по зарезервированному адресу
    ПоместитьВоВременноеХранилище(ТаблицаРезультат, АдресЗаписи);
КонецПроцедуры

// Функция извлечения данных из хранилища родительским сеансом
Функция ПолучитьРезультатФоновогоЗадания(АдресЗаписи) Экспорт
    Возврат ПолучитьИзВременногоХранилища(АдресЗаписи);
КонецФункции

Шаг 3. Реализация логики в модуле Обычной формы

В модуле обычной формы мы генерируем идентификатор при открытии, запускаем фоновое задание по кнопке и по таймеру (или по другой кнопке) проверяем статус выполнения.


Перем КлючСвязиСеансов;
Перем МассивАктивныхЗаданий;

Процедура ПриОткрытии()
    // Генерируем уникальный идентификатор для текущего экземпляра формы
    КлючСвязиСеансов = Новый УникальныйИдентификатор;
    МассивАктивныхЗаданий = Новый Массив;
КонецПроцедуры

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

// Обработчик получения результатов (например, вызываемый по обработчику ожидания)
Процедура КнопкаПолучитьРезультатНажатие(Элемент)
    
    Для Каждого ТекЗадание Из МассивАктивныхЗаданий Цикл
        
        // Получаем актуальный статус фонового задания
        ЗаданиеОбъект = ФоновыеЗадания.НайтиПоУникальномуИдентификатору(ТекЗадание.Задание.УникальныйИдентификатор);
        
        Если ЗаданиеОбъект.Состояние = СостояниеФоновогоЗадания.Завершено Тогда
            
            // Получаем результат из временного хранилища через сервер
            РезультатТаблица = ФоновыеПроцедурыСервер.ПолучитьРезультатФоновогоЗадания(ТекЗадание.АдресВХ);
            
            // Не забываем удалить данные из ВХ для экономии оперативной памяти сервера
            УдалитьИзВременногоХранилища(ТекЗадание.АдресВХ);
            
            Если РезультатТаблица <> Неопределено Тогда
                Сообщить("Данные успешно получены! Количество строк: " + Строка(РезультатТаблица.Количество()));
                // Дальнейшая обработка полученной ТаблицыЗначений на клиенте
            КонецЕсли;
            
        ИначеЕсли ЗаданиеОбъект.Состояние = СостояниеФоновогоЗадания.Активно Тогда
            Сообщить("Задание еще выполняется. Повторите попытку позже.");
        Иначе
            Сообщить("Задание завершилось аварийно или было отменено администратором.");
        КонецЕсли;
        
    КонецЦикла;
    
КонецПроцедуры

---

Критически важный нюанс: Утечка памяти на сервере 1С

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

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

В Обычном приложении автоматической очистки не происходит! Если вы передали вручную сгенерированный Новый УникальныйИдентификатор, платформа считает его независимым. Такие данные будут висеть в оперативной памяти сервера приложений 1С до полного завершения сеанса пользователя. Если пользователи работают в системе весь день и активно запускают фоновые задания, генерирующие тяжелые таблицы значений, серверный процесс быстро исчерпает доступную оперативную память, что приведет к падению рабочих процессов (rphost).

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


Процедура ПриЗакрытии()
    // Принудительно очищаем адреса временного хранилища при закрытии окна пользователем
    Для Каждого ТекЗадание Из МассивАктивныхЗаданий Цикл
        Попытка
            УдалитьИзВременногоХранилища(ТекЗадание.АдресВХ);
        Исключение
            // Игнорируем ошибку, если данные уже были удалены ранее
        КонецПопытки;
    КонецЦикла;
КонецПроцедуры

---

Влияние обновлений платформы (начиная с 8.3.12 и выше)

Проанализируем изменения в поведении временного хранилища в современных релизах платформы, о которых важно помнить:

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

← На главную