1с параметр фонового задания не поддерживает сериализацию

Обновлено: 05.07.2024

Длительные операции на сервере

Область применения: управляемое приложение.

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

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

В противном случае такие вызовы могут привести к потере работоспособности приложения или затруднению работы с ним:

  • браузер может предложить прекратить длительно выполняющийся сценарий, после чего приложение станет неработоспособным;
  • веб сервер может прервать длительное обращение к серверу 1С:Предприятия и вернуть ошибку 504 (шлюз не отвечает);
  • в случае длительного выполнения операции, у пользователя нет возможности отменить ее.

2.1. Общий подход к асинхронному выполнению длительных серверных операций с помощью фонового задания:

    Код, выполняющий длительную обработку данных, располагается в модуле менеджера объекта* или в общем модуле. Результат своей работы он помещает во временное хранилище;


а для прочих мест – выводится блокирующая форма ( РежимОткрытияОкна = БлокироватьОкноВладельца ), на которой размещена декорация с анимированной картинкой и кнопка «Отмена» :

2.2. Асинхронное формирование отчета требуется только для тех отчетов, которые

  • разработаны без использования СКД или с использованием СКД, но с переопределенной процедурой формирования отчета (переопределен обработчик кнопки «Сформировать» или в обработчике модуля отчета ПриКомпоновкеРезультата устанавливается СтандартнаяОбработка = Ложь ).
  • и формирование которых, как правило, занимает длительное время.

Поведение таких отчетов должно быть максимально похожим на поведение отчетов на базе СКД, а именно:

  • форму отчета не следует блокировать на время его формирования;
  • пользователь может изменить настройки и переформировать отчет, не дожидаясь окончания его формирования;
  • при закрытии формы отчета, формирование отчета прерывается.

3. При использовании в конфигурации Библиотеки стандартных подсистем в распоряжении разработчика имеются вспомогательные функции и процедуры общих модулей ДлительныеОперации , ДлительныеОперацииКлиент , а также процедура УстановитьСостояниеПоляТабличногоДокумента общего модуля ОбщегоНазначенияКлиентСервер .

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

Функция ОпределитьНастройкиУчетнойЗаписи(АдресЭлектроннойПочты, Пароль) Экспорт
.
Возврат Настройки;
КонецФункции

В форме объекта выполняется вызов этой функции в фоновом задании в три этапа:
1) запуск фонового задания на сервере,
2) подключение обработчика завершения фонового задания на клиенте,
3) обработка результата выполнения фонового задания.

&НаКлиенте
Процедура НастроитьПараметрыПодключенияАвтоматически()
// 1. Запуск фонового задания на сервере.
ДлительнаяОперация = НачатьПоискНастроекУчетнойЗаписи();

// 2. Подключение обработчика завершения фонового задания.
ПараметрыОжидания = ДлительныеОперацииКлиент.ПараметрыОжидания(ЭтотОбъект);
Оповещение = Новый ОписаниеОповещения("ПриЗавершенииПоискаНастроек", ЭтотОбъект);
ДлительныеОперацииКлиент.ОжидатьЗавершение(ДлительнаяОперация, Оповещение, ПараметрыОжидания);
КонецПроцедуры

&НаСервере
Функция НачатьПоискНастроекУчетнойЗаписи()
ПараметрыВыполнения = ДлительныеОперации.ПараметрыВыполненияФункции(УникальныйИдентификатор);
Возврат ДлительныеОперации.ВыполнитьФункцию(ПараметрыВыполнения, "Справочники.УчетныеЗаписиЭлектроннойПочты.ОпределитьНастройкиУчетнойЗаписи",
АдресЭлектроннойПочты, Пароль);
КонецФункции

// 3. Обработка результата выполнения фонового задания.
&НаКлиенте
Процедура ПриЗавершенииПоискаНастроек(Результат, ДополнительныеПараметры) Экспорт

Если Результат = Неопределено Тогда // Пользователь отменил задание.
Возврат;
КонецЕсли;

Если Результат.Статус = "Ошибка" Тогда
ВызватьИсключение Результат.КраткоеПредставлениеОшибки;
КонецЕсли;

Настройки = ПолучитьИзВременногоХранилища(Результат.АдресРезультата);
УдалитьИзВременногоХранилища(Результат.АдресРезультата);
УстановитьНастройкиУчетнойЗаписи(Настройки);

Методическая рекомендация (полезный совет)

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

ПараметрыВыполнения = ДлительныеОперации.ПараметрыВыполненияФункции(УникальныйИдентификатор);
ДлительныеОперации.ВыполнитьФункцию(ПараметрыВыполнения, ПараметрФоновогоЗадания);

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

Настройки = ПолучитьИзВременногоХранилища(Результат.АдресРезультата);
УдалитьИзВременногоХранилища(Результат.АдресРезультата); // Данные во временном хранилище больше не требуются.

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

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

&НаСервере
Функция НачатьПоискНастроекУчетнойЗаписи()
ПараметрыВыполнения = ДлительныеОперации.ПараметрыВыполненияФункции(УникальныйИдентификатор);
ПараметрыВыполнения.АдресРезультата = АдресРезультатаФоновогоЗадания; // всегда используем одно и то же временное хранилище

Возврат ДлительныеОперации.ВыполнитьФункцию(ПараметрыВыполнения,
"Справочники.УчетныеЗаписиЭлектроннойПочты.ОпределитьНастройкиУчетнойЗаписи",
АдресЭлектроннойПочты, Пароль);
КонецФункции

4. Если в конфигурации реализуются алгоритмы, инициирующие запуск фоновых заданий или запись данных информационной базы без участия пользователя (например, регулярное обновление информации в открытой форме), то в них следует проверять, что в текущем сеансе не установлен монопольный режим. В противном случае, следует блокировать попытки выполнения таких действий. Например:

Если МонопольныйРежим() Тогда
Возврат;
КонецЕсли;

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

Обновление данных ИБ при первом интерактивном запуске программы после обновления конфигурации; Выгрузка данных информационной базы в файл для перехода в сервис; Использования монопольного режима для снижения времени выполнения массовых операций по изменению данных;

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

На время выполнения этого фонового задания следует блокировать весь интерфейс приложения, открывая форму ожидания завершения операции в режиме РежимОткрытияОкна = БлокироватьВесьИнтерфейс. Блокировать интерфейс приложения требуется потому, что на время выполнения задания полноценная работа пользователя с приложением уже невозможна:

  • Если пользователь(*) попытается записать какой-либо объект, это приведет к ошибке (из-за установленного монопольного режима);
  • В ряде случаев могут запускаться фоновые задания в качестве реакции на действия пользователя случае (при поиске в динамическом списке, при вводе по строке, формировании отчетов и пр.), которые также завершатся с ошибкой.
    Кроме того, на самой форме ожидания длительной операции не следует размещать элементы управления, которые могут приводить к запуску таких фоновых заданий. Например: поля ввода, динамические списки и отчеты.

* Примечание: ошибки записи также возникают в тех случаях, когда объекты записываются программно, например, из обработчиков ожидания. В них также следует проверять монопольный режим согласно п.5.

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

И ещё кстати. Почему не заставить РЗ самому получить эти 12 лямов строк?
(3) Этот вариант оч. долго согласовывать, и скорее всего не одобрят, плюс доступы на SQL выпрашивать. И не совсем понятно как объектные сущности в таблице значений разложить в таблице SQL много шаманства по моему будет
(8) формально он у тебя есть - ты же в базу ходишь как-то

>И не совсем понятно как объектные сущности в таблице значений разложить в таблице SQL много шаманства по моему будет

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

(12) Мне они не нужны, табличка из типового проведение себестоимости УПП (РАУЗ)
(7) Если РЗ=ФЗ, то думал об этом, но к сожалению в моей задаче это не сработает
(15) Передается из одной процедуры выполняемом в серверном общем модуле, в фоновое задание, которое то же на сервере выполняется.
(16) Сериализация-десериализация ТЗ способны устроить кошмар любой системе.

>Как эта ТЗ формируется? Очень сложно?

запускают чего-то типа расчет себестоимости - и привет

тз ->хз. можно еще попробовать хз в строку внутреннюю.

либо тз с хз и в временный файл.

было не так давно в одной отраслевой упп.

модуль проведение документа.

внутри 2-го обход ТЧ и набивание ТЗ построчно.

теперь простая арифметика:

ТЗ = 10 000 000 строк

клиент "думает" до 2 Гб и падает

что делает клиент? звонит во франь, консультант франя заходит по rdp на сервак и (о чудо!) проводит документ. За 10 минут. Сервак 64-бит. А значит 3,5 Гб на клиента скребя ложкой по дну ему хватает.

какой вывод сделал франь?

купите 64-бит сервер 1с.

(28) так дело в том, зайка моя, что от него толку в (25, 27) будет 0 без переписывания кода.
(17) Запись движений в базу (сколько строк столько и движений) :)
+(33) например, в фоновое передавать только организацию, подразделение и там группу какой-нить номенклатуры, а таблицу оборотов (предположим, что речь про обороты) получать из базы уже там
(33) нет, так как запрос делается по движениям в базе, которые потом либо меняются либо удаляются (после запроса), если один фон начнет менять движения, а второй начнет их только читать перед подготовкой ТЗ, то в итоге они будут работать с разными таблицами значений
(35) Пусть фоны читают ТЗ и ждут, пока все флаги в служебном регистре сведений не установятся в "Можно писать"
(37) управляемый режим давно уже включен, "ДЛЯ ИЗМЕНЕНИЯ" игнорится, наверное можно управляемую блокировку установить, только по таймауту тож легко вылететь
(36) Наверное в регистр в ресурс с типом ХранилищеЗначения проще положить саму табличку, спасиб за идею с регистром, ща буду пробовать

(38) При положении в ХЗ пойдет сериализация в XDTO + кодирование в base64, которое, я боюсь, выполнено через опу..

При извлечении из ХЗ пойдет обратный процесс.

(41) На тот невероятный случай, когда один процесс прочтет и запишет данные раньше, чем запустится другой процесс.
21-й век на дворе, а все возитесь с таблицами значений?
Пригласите специалиста.
(29) нет, зачем франч предложил купить 64бит сервер, если он уже есть? на который консультант зашел через рдп.
(41) Пишется и читается вне транзакции, блокировки вообще не сработают, плюс (если делать в транзакции) большая вероятность таймаута

(31) распараллеливается запись одного набора?
и в наборе 12 миллионов строк?

что-то я не очень понял.

странно что все пытаются придумать что делать с тз, вместо того, чтобы удостовериться в том, что в принципе выбрана правильная методика решения задачи.

(50) тогда почему нельзя приготовить отдельные наборы с меньшим числом записей на базе этой таблицы, и передавать уже их?
(35) разбей данные на не пересекающиеся куски и читай-пиши, сколько угодно и когда угодно
(51) Мне то они не нужны, типовой расчет себестоимости в упп 1.3, по методологии расчета вопрос тож не ко мне, не я ее утверждал.
(40) Да, не получилось в ХранилищеЗначения в рег. сведений поместить, 200 тыс. записей легко прошли сек. за 20, а на 5 млн отвалилось по ошибке потока (
(58) Вы пытаетесь сделать какое-то "еврейское" распараллеливание - думаю, что ничего у Вас не выйдет. Предполагаемая модель для распределения задач по "потокам" - убога и нежизнеспособна.
Вместо того, чтобы пытаться с помощью "молотка и такой-то матери" как-то передать ТЗ "потоку" - гораздо перспективней будет подойти к вопросу с другой стороны.
Проанализировать алгоритм, определить на какие атомарные операции его можно разбить - и уже от этого "плясать"

(59) Угумс. Типы, не имеющие XDTO-сериализации - прекрасно помещаются в хранилище значения. Есть и обратные примеры - когда тип помещается в хранилище, но не имеет XDTO-сериализации.

Исследования методом "научного тыка" показали, что, помещение в хранилище значения - эквивалентно ЗначениеВСтрокуВнутр(), а не XDTO-сериализации.

(60) Все такие умные, что ППЦ.

Ну, разбей хотя бы алгоритм удаления движений по нескольку регистров на поток - заипешься код писать, а потом (самое главное) ошибки ловить. Это примитив.

А мы тут про расчет себестоимости.

+(61) Обратные примеры - когда тип имеет XDTO-сериализацию, но в хранилище значения не помещается. Очепятался
(62) Многопоточность это вообще сложно.
(64) И что? Есть типы, которые ее не имеют - но в хранилище помещаются. И наоборот, есть типы которые не помещаются в хранилище, но пишутся в XDTO

(61)
Исследования методом "научного тыка" показали.

(66) Вкратце - перебирались почти все типы встроенного языка (вплоть до ОбъектМетаданных, ИсторияРаботыПользователей, РасширенноеИмяXML - и прочего безобразия).

Для каждого типа делалась попытка:

(68) Масштабно, епта.
Результаты опубликуй.

ТЗ В XDTO есть? Нет? А если найду?!

(69) Если найду - опубликую.
Еще выяснилось, что просто ЗаписатьXML() - который метод глобального контекста - тоже сильно разнится с СериализаторXDTO.ЗаписатьXML().

ТЗ в XDTO есть :) Но это не значит, что помещение в хранилище значения - есть XDTO

(70)
ЗаписатьXML() - который метод глобального контекста - тоже сильно разнится с СериализаторXDTO.ЗаписатьXML().

(71) Это имеет не очень не много смысла.
Подозреваю, что просто ЗаписатьXML() от 8.0 остался, пока XDTO не было.
(0) Я через выгружал в текстовый файл и булками загружал во временную таблицу.
(71) Ты случайно с ЗаписатьXDTO не путаешь?

(68) Дааа, ты чертовски прав..
1) Создал справочник с реквизитом ХЗ.

Простите за кодировку, было лениво морочиться.

Млядские строки, неужели нельзя в ХЗ сохранять кусок памяти.
(76) Так там не один кусок памяти, а набор объектов (строки ТЗ, колонки) содержащих друг на друга, наверно. Может стоит указать объект СжатиеДанных?
(76) Кстати а как насчет использования ЗаписьFastInfoset

(76) Вот тут я конечно тот еще басист - но что-то мне подсказывает, что если просто сохранять кусок памяти - то его потом будет оооочень сложно прочитать следующей версией, когда в плюсовом классе будет изменен состав членов.

Например, в той же Java - если сделать implements Serializable и не реализовывать свои writeObject и readObject - то именно так и получится. А если реализовывать writeObject и readObject - то это уже не будет "просто кусок памяти".

(79) Все равно будет строка.
В которую будут преобразованы двоичные данные :)
(74)
Вы забыли указать степень сжатия при создании хранилища.

(83) protobuf, как и json - всего-лишь форматы. Ничего нового в такое понятие как сериализация они не приносят.

К тому же, даже из вашей ссылки на вики:

> По замыслу разработчиков сначала должна быть описана
> структура данных, которая затем компилируется в классы,

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

На основании объекта класса строится DTO-представление, которое уже и пишется файл.

(0) ИМХО - Все намного проще делается.
Не лучше все движения запихнуть во временную таблицу с полем порядковый номер. И забирать уже из временной по условию (от 1 до милиона) - первый поток, от милиона+1 до двух милионов - второй поток и тд.
(86) Для этого всего-лишь нужно умудриться передать в другой сеанс менеджер временных таблиц)
(86) Каким же образом из разных сеансов (фоновых заданий) получать временную таблицу созданную в родительском сеансе?
(82) Я не сомневался в тебе. Сжатие данных скорее всего сожмет эту строку deflate-ом.
(85) Если это массив структур то можно взять и кусок памяти, так как они храняться все в одном непрерывном куске.
Что касается объектов, то они разбросаны по всей памяти.
Таблица типизирована, то можно записывать как в DBF для данных с определенным размером либо указывая размер данных в первых байтах например для строк.
Либо записывать в аналог иерархической бд
(93) Я не соглашусь с тобой.
Имхается мне, что все объекты в 1С - связные списки (даже массивы и структуры) - нет падения скорости вставки на больших объемах.

Мегазадача. а если попробовать как-то получать из разных сеансов адрес ячеек в ОЗУ, занятых исходной таблицей (которую инициировал родительский сеанс)?

(95) Аххх, если бы узнать адреса объектов 1С. мммм, какие возможности, но. Мечты. мечты.

Можно создавать локальные и глобальные временные таблицы. Локальные временные таблицы видимы только во время текущего сеанса, а глобальные — во всех сеансах. Временные таблицы не подлежат секционированию.

Инструкции SQL могут обращаться к временной таблице по заданному в инструкции CREATE TABLE значению аргумента table_name, например:

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


Что у нас в 1с Предприятии 8.2 имеется для распараллеливания & это фоновые задачи. Метод, который будет вызываться в фоновой задаче, должен быть прописан в серверном общем модуле и быть экспортным. Естественно нам понадобиться в фоновую задачу передавать и забирать значения.

Зачем нам в фоновую задачу передавать адрес во временном хранилище. Наша фоновая задача должна куда-то положить результат, причем так чтобы мы знали где его потом взять.

Для того чтобы запустить фоновые задачи выполняется следующий код:

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

ФоновыеЗадания.ОжидатьЗавершения(МассивЗапущенныхЗаданий);

После того как все задачи были завершены, можем приступить к получению из них данных. Для этого мы проходим по всем адресам в хранилище, которые хранятся в массиве МассивАдресовВХранилище. После получения результата фонового задания перегоняем его в общую таблицу.

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

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


Что у нас в 1с Предприятии 8.2 имеется для распараллеливания & это фоновые задачи. Метод, который будет вызываться в фоновой задаче, должен быть прописан в серверном общем модуле и быть экспортным. Естественно нам понадобиться в фоновую задачу передавать и забирать значения.

Зачем нам в фоновую задачу передавать адрес во временном хранилище. Наша фоновая задача должна куда-то положить результат, причем так чтобы мы знали где его потом взять.

Для того чтобы запустить фоновые задачи выполняется следующий код:

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

ФоновыеЗадания.ОжидатьЗавершения(МассивЗапущенныхЗаданий);

После того как все задачи были завершены, можем приступить к получению из них данных. Для этого мы проходим по всем адресам в хранилище, которые хранятся в массиве МассивАдресовВХранилище. После получения результата фонового задания перегоняем его в общую таблицу.

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

Читайте также: