Почему временное хранилище возвращает Неопределено при работе с фоновыми заданиями?

Программист IT и автоматизация бизнеса
← На главную

При разработке механизмов многопоточности в 1С:Предприятии 8.3 программисты часто сталкиваются с ситуацией, когда данные, переданные из фонового задания через временное хранилище, оказываются недоступны в основном сеансе. Особенно остро эта проблема проявляется в клиент-серверном варианте работы, в то время как в файловой базе всё работает корректно с первого раза. Проанализируем, почему так происходит, и как правильно организовать получение результата выполнения фонового задания.

Причина возникновения проблемы: особенности клиент-серверной архитектуры

Разберем ситуацию, описанную разработчиками: при первом запуске фонового задания система возвращает Неопределено, а при повторном — корректное значение. Выясним причину такого поведения. В клиент-серверном режиме фоновое задание выполняется в рамках отдельного сеанса на сервере приложений. Когда задание завершает свою работу и записывает данные во временное хранилище, эти данные должны быть синхронизированы с основным сеансом пользователя.

Проблема «первого раза» и случайных пропусков данных связана с микроскопической задержкой (так называемый race condition) между моментом, когда кластер регистрирует статус задания как Завершено, и физическим завершением процесса репликации данных временного хранилища между сеансами. В файловом варианте сеанс фактически один (или доступ к памяти прямой), поэтому данные видны мгновенно. В клиент-серверном режиме требуется время на «прокачку» данных из сеанса фонового задания в сеанс-владелец.

Анализ типового (ошибочного) кода

Рассмотрим пример кода, который часто приводит к подобным ошибкам в обычных формах:


// Модуль обработки (Обычные формы)
Перем Ключ, АдресРезультата;

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

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

Решение №1: Использование правильного владельца хранилища

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

Заменим инициализацию адреса на следующий код:


// В обычном приложении у формы есть свойство УникальныйИдентификатор
АдресРезультата = ПоместитьВоВременноеХранилище(Неопределено, ЭтаФорма.УникальныйИдентификатор);

Решение №2: Корректная фильтрация заданий

Проанализируем метод ФоновыеЗадания.ПолучитьФоновыеЗадания(). Если фильтровать только по имени и ключу, система может вернуть массив, содержащий старые, уже завершенные задания с таким же ключом (если обработка запускалась ранее). В этом случае код может попытаться прочитать данные из хранилища старого задания, которые уже были удалены платформой по истечении времени жизни.

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


// В модуле формы
Перем ИдентификаторЗадания;

Процедура КнопкаВыполнитьНажатие(Кнопка)
    // ... подготовка параметров ...
    Задание = ФоновыеЗадания.Выполнить("Сервер.Сформировать", Параметры, Ключ, "Задание");
    ИдентификаторЗадания = Задание.УникальныйИдентификатор;
    ПодключитьОбработчикОжидания("ПроверкаГотовности", 0.5, Истина);
КонецПроцедуры

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

Решение №3: Метод ОжидатьЗавершения

Если логика работы позволяет на короткое время заблокировать интерфейс или если вызов происходит в серверном контексте, наиболее стабильным способом является использование метода ФоновыеЗадания.ОжидатьЗавершения(). Платформа 1С при выходе из этого метода гарантированно выполняет синхронизацию всех связанных данных временного хранилища.

Посмотрим на пример реализации:


МассивЗаданий = Новый Массив;
МассивЗаданий.Добавить(НужноеФоновоеЗадание);

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

Рекомендации по стабильной работе

Для минимизации ошибок при работе с фоновыми заданиями в клиент-серверном режиме придерживайтесь следующих правил:

  1. Не используйте случайные UUID для создания адреса во временном хранилище. Всегда привязывайте адрес к идентификатору формы или другого долгоживущего объекта.
  2. Проверяйте результат на Неопределено даже после того, как задание получило статус Завершено. Если данные еще не доступны, сделайте паузу в 0.1–0.2 секунды и повторите попытку получения.
  3. Избегайте передачи больших объемов данных через параметры фонового задания обратно. Если данных много, лучше сохранить их во временную таблицу базы данных или файл.
  4. Используйте актуальные платформы. Описанная проблема часто встречалась на версиях 8.3.8–8.3.12. В современных версиях (8.3.18+) механизмы синхронизации сеансов работают значительно быстрее и стабильнее.

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

← На главную