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