Обработчик ожидания. Нюансы

24.08.21

Разработка - Механизмы платформы 1С

ПодключитьОбработчикОжидания: некоторые подробности и особенности работы

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

 

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

С точки зрения платформы, это поддерживаемая процессом rphost, единая для сеанса, виртуальная таблица ключей, таймаутов и текущих значений таймера.
* Ключ - хеш от ID модуля, откуда был выполнен вызов, ID модуля вызываемой процедуры обработчика, и имени процедуры обработчика. Идентификаторы модулей можно увидеть, выгрузив конфигурацию в файлы.
* Таймаут - число, рассчитывается в миллисекундах.
* Текущее значение таймера - момент предыдущего срабатывания, дата/время с миллисекундами. Таймеры отталкиваются от текущей даты/времени сеанса в момент компиляции подключения обработчика или реинициализации таблицы.

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

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

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

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

Существенно различно в разных релизах до 8.3.11 ведёт себя при остановке на точке останова; в текущих релизах всегда сбрасываются таймауты и сразу стартует обработка заново - и по прохождении останова, и при остановке по ошибке, если ошибка была обработана. Не важно, где находится точка останова - в действиях, вызванных обработчиком, или в других действиях, реинициализация делается всегда.

Поведение аналогично и для файловых, и для клиент-серверных ИБ. Поведение в клиенте тестирования аналогично поведению в обычных запусках клиента. Поведение в веб-клиенте формально одинаковое, но известны случаи, когда в браузере "Safari" таймауты "сжимались" относительно ожидаемого в состоянии "покоя" клиента, т.е. написано было 60, а реально срабатывало через 45-48 секунд.

Сеансы и обработчики. До версии 8.3.7.2008 наличие хотя бы одного сработавшего обработчика ожидания вынуждало сеанс никогда не "засыпать" и даже не считаться пассивным. В некоторых релизах 8.3.9 и 8.3.11 запущенные обработчики не мешали сеансу засыпать и быть впоследствии удалённым, но продолжали работать, что проверено выполнением записи из их процедуры в клиентский файл на локальном ПК (занята ли бывала при этом лицензия, сведений нет) - фиксировать активность сеанса средствами ЖР нельзя было потому, что запись в ЖР это обращение к сервису кластера и вообще к серверу. 

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

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

Из устройства этого механизма следует ряд нюансов.

1. Обработчик можно запускать и останавливать в форме, экземпляр которой создан, но не открыт; в любой момент, пока её контекст есть в некоей переменной. Запускать и останавливать придётся отдельными экспортными процедурами, т.к. обращение "НеоткрытаяФорма.ПодключитьОбработчикОжидания" игнорируется без генерации исключения. С уничтожением переменной формы останавливается и обработчик - поэтому следует быть внимательными, чтобы после закрытия запустившей формы или команды не случилось зависания этой переменной в памяти сеанса.

 
 Пример


2. Не-клиентские и не-контекстные процедуры не могут обрабатываться. Логично - в таблице тайминга ориентируемся на хеш, связанный с контекстом. Процедура не должна иметь даже необязательные параметры, и быть экспортной. Также, может быть подключена функция; результат её выполнения игнорируется.

3. В качестве запускаемых обработчиком могут быть процедуры модуля приложения, любого глобального общего модуля, для которого указано исполнение на клиенте (в т.ч. наряду с исполнением на сервере или во внешнем соединении). Если модуль не "чисто" клиентский, по умолчанию директивы компиляции и инструкции препроцессора исполняемой процедуре всё равно не нужны - при наличии флага "Клиент" для модуля обработчик ожидания все его "подходящие" процедуры считает клиентскими. Но, конечно, желательно всё указывать в явном виде. Важно! Запуск и остановка всегда должны находиться в контексте одного модуля, иначе запустить - запустит, но потом не остановит. Это могут быть модуль приложения, глобальные и неглобальные общие модули, модули объектов и модули менеджеров - главное, чтобы вызов старта и остановки происходил в одном модуле.
В модуле приложения подключение можно выполнять в любом системном событии - в ПередНачаломРаботыСистемы оно уже корректно запустится, а в событиях завершения работы системы будет проигнорировано.

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

5. Сколько бы раз ни было запущено подключение обработчика некоей процедуры, учитывается лишь последний запуск - он "перебивает" предыдущие. Повторное/многократное отключение обработчика игнорируется без генерации исключения. В том числе и если обработчик вообще не был запущен.

 
 Пример


6. Из процедуры, вызванной обработчиком, можно повторно подключать, переподключать с иным таймаутом, останавливать этот обработчик. Например, можно эмулировать однократный запуск - допустим, условие определяется при срабатывании; можно эмулировать многократный запуск-вызов с таймаутом менее 1 секунды. В этом случае, т.е. если внутри процедуры-обработчика однократного таймаута находится повторное её же подключение, получается практически мгновенный и при необходимости бесконечный самовызов. Зависаний и торможений, если в рабочей части вызванного нет "тяжёлых" действий, не замечено.

 
 Пример


7. При одинаковом таймауте, обработчики модуля приложения и глобальных модулей более приоритетны при опросе и срабатывании, чем обработчики форм. Между собой обработчики модуля приложения и глобальных модулей "равноправны", формы между собой - тоже. Это означает, что и для однократного, и для постоянного вызова всегда сначала отрабатывает "глобальный", потом "форменный". Независимо от порядка подключения обработчиков, если они были подключены за одну компиляцию фрагмента кода (это хорошо заметно на примере однократных вызовов с таймаутом 0.1). А вот при равных "правах" вызовы отрабатывают по порядку инициализации-подключения их обработчиков.

8. Таймауты, имеющие дробные значения секунд, корректно обрабатываются независимо от значения флага однократности (это легко проверить с помощью ТекущаяУниверсальнаяДатаВМиллисекундах), с точностью до десятых.

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

10. Редко, но используется запуск средствами СОМ полноценного приложения V83.Application. При таком запуске расположенные в модуле приложения обработчики запускаются и живут до конца сеанса, независимо от видимости приложения. Возможен запуск КомОбъект.ПодключитьОбработчикОжидания (справедливо и для модуля приложения, и для форм, хотя в этом случае работа с формами уже совсем фантастика) и остановка аналогично. Важно, что в этом случае при закрытии запускавшего контекста (и формы, и самого запустившего сеанса) запущенный продолжает работать, что при Visible=Ложь кончится снятием через диспетчер задач. В некоторых релизах сеанс, применительно к ком-объекту которого делали вызов, при закрытии приложения выдаёт невосстановимую ошибку.

 

p.s. Сведения о ёмкости служебной таблицы запущенных обработчиков найти не удалось, т.е. сколько максимум может быть таких обработчиков в сеансе, или в конкретном контексте, данных нет. Эксперимент со 100 разными процедурами с одним или разными таймаутами показал правильную штатную работу.


Я наверняка многое упустил; дополнения и замечания приветствуются.

Использовались релизы 8.3.6.2237, 8.3.18.1289 и 8.3.19.1150

 

ПодключитьОбработчикОжидания обработка ожидания особенности нюансы

См. также

Сервисы интеграции без Шины и интеграции

Механизмы платформы 1С Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Пример использования «Сервисов интеграции» без подключения к Шине и без обменов.

13.03.2024    2693    dsdred    16    

59

Поинтегрируем: сервисы интеграции – новый стандарт или просто коннектор?

Перенос данных 1C Администрирование СУБД Механизмы платформы 1С Платформа 1С v8.3 Бесплатно (free)

В платформе 8.3.17 появился замечательный механизм «Сервисы интеграции». Многие считают, что это просто коннектор 1С:Шины. Так ли это?

11.03.2024    6225    dsdred    59    

86

Как готовить и есть массивы

Механизмы платформы 1С Платформа 1С v8.3 Бесплатно (free)

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

24.01.2024    6106    YA_418728146    25    

68

Планы обмена VS История данных

Перенос данных 1C Механизмы платформы 1С Платформа 1С v8.3 Бесплатно (free)

Вы все еще регистрируете изменения только на Планах обмена и Регистрах сведений?

11.12.2023    7167    dsdred    36    

114

1С-ная магия

Механизмы платформы 1С Бесплатно (free)

Язык программирования 1С содержит много нюансов и особенностей, которые могут приводить к неожиданным для разработчика результатам. Сталкиваясь с ними, программист начинает лучше понимать логику платформы, а значит, быстрее выявлять ошибки и видеть потенциальные узкие места своего кода там, где позже можно было бы ещё долго медитировать с отладчиком в поисках источника проблемы. Мы рассмотрим разные примеры поведения кода 1С. Разберём результаты выполнения и ответим на вопросы «Почему?», «Как же так?» и «Зачем нам это знать?». 

06.10.2023    19285    SeiOkami    46    

119

Дефрагментация и реиндексация после перехода на платформу 8.3.22

Механизмы платформы 1С Платформа 1С v8.3 Бесплатно (free)

Начиная с версии платформы 8.3.22 1С снимает стандартные блокировки БД на уровне страниц. Делаем рабочий скрипт, как раньше.

14.09.2023    12998    human_new    27    

76

Валидация JSON через XDTO (включая массивы)

WEB-интеграция Универсальные функции Механизмы платформы 1С Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

При работе с интеграциями рано или поздно придется столкнуться с получением JSON файлов. И, конечно же, жизнь заставит проверять файлы перед тем, как записывать данные в БД.

28.08.2023    9602    YA_418728146    6    

143

Внешние компоненты Native API на языке Rust - Просто!

Механизмы платформы 1С Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Внешние компоненты для 1С можно разработывать очень просто, пользуясь всеми преимуществами языка Rust - от безопасности и кроссплатформенности до удобного менеджера библиотек.

20.08.2023    6596    sebekerga    54    

95
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. SAShikutkin 5 24.08.21 15:18 Сейчас в теме
Неплохо было бы разбавить голый текст примерами. Читать не интересно.
romulanin; Bob_Dobr; magic1s; voneska7; Dmitri93; user1725319; elga2012; Rikpa; kako1toxren; bulpi; frkbvfnjh; maksa2005; awk; kser87; CyberCerber; BigB; triviumfan; +17 2 Ответить
6. frkbvfnjh 787 25.08.21 10:35 Сейчас в теме
(1) Автор, просим примеров, тема оказывается не простая
2. kser87 2441 25.08.21 09:32 Сейчас в теме
7. Yashazz 4727 25.08.21 10:43 Сейчас в теме
(2) Жаль, если так. Принято.
AlexiyNA; kser87; +2 Ответить
3. booksfill 25.08.21 09:40 Сейчас в теме
Спасибо. Очень интересно.

Но, действительно, почему-бы не поделиться примерами?

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

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

Но, право слово, лучше бы увидеть пример подобного зависания.
5. Yashazz 4727 25.08.21 09:54 Сейчас в теме
(3) Поясняю: переменная формы это переменная, хранящая значение типа "ФормаКлиентскогоПриложения" (УФ); если она засунута куда-то не очень очевидно, то выгрузка формы из памяти сеанса не происходит, об этом одно время много писали.
Когда обработчик ожидания остановлен, проблем нет, но когда он работает, то начинаются "спецэффекты". А засунуть могли куда и как угодно. Например:
ОписОповещения = Новый ОписаниеОповещения("НекоеДействие", ЭтотОбъект); // теперь его Модуль это наша УФ, а судьба этого описания оповещения в дебрях клиентских и не очень модулей может быть весьма заковыриста
// или тупо
ОткрытьФорму("ОбщиеФормы.НекаяФорма", , ЭтотОбъект); // теперь её ВладелецФормы это наша УФ.
4. Yashazz 4727 25.08.21 09:47 Сейчас в теме
Да я не против примеров, просто не успел ещё. Добавлю.
AlexK_2012; frkbvfnjh; +2 Ответить
8. AntonProgma 46 25.08.21 12:23 Сейчас в теме
10. Yashazz 4727 25.08.21 13:21 Сейчас в теме
(8) Не сказал бы, по сравнению со многими другими - вполне вменяемый механизм платформы.
11. AntonProgma 46 25.08.21 14:38 Сейчас в теме
(10) когда нужно добавлять глобальный модуль для процедуры, которая нужна только чтобы вызвать другую процедуру спустя таймаут - это кривая хрень. И отсутствие возможности передать параметры туда же. Я понимаю, что у 1с особый путь и неповторимый почерк, но по сути это кривая хрень.
user1878860; +1 Ответить
12. Yashazz 4727 25.08.21 14:56 Сейчас в теме
(11) Модуль и так есть, модуль приложения. Он, конечно, тоже не без изъянов, но есть. А вот что параметры передать - да, согласен, явная недоработка, могли бы поступить, как с фоновым заданием, или хотя бы как с описанием оповещения. Хотя б немутабельные сериализуемые, уже было б легче. Но, что имеем...
13. AntonProgma 46 25.08.21 15:08 Сейчас в теме
(12) фоновые задания и описания оповещения тоже хрень разной степени кривизны). Забавно смотреть, как одной головой 1с создаёт "фишки", а другой - пытается их обойти в типовых решениях. "ЭтаПеременнаяЧтобыНеРугаласьПроверкаКонфигурацииНаПустойОбр­аботчик"
14. Yashazz 4727 25.08.21 15:15 Сейчас в теме
(13) О дааа. Я больше скажу - некоторые изменения в платформе происходят тогда, когда под окнами офиса разрабов платформы собирается достаточно большая и агрессивная толпа разрабов конфигурации)))
AntonProgma; +1 Ответить
9. Octopus 337 25.08.21 12:36 Сейчас в теме
Информация интересная, много неожиданного. Но текст тяжело воспринимается, громоздкие формулировки. Ну и да, примеров бы еще
nekit_rdx; Serg O.; +2 Ответить
15. aleksey2 86 26.08.21 08:23 Сейчас в теме
как распечатать список текущих обработчиков?
17. Yashazz 4727 26.08.21 11:57 Сейчас в теме
(15) Скажем более общо - как его получить. Навскидку - не знаю. В языке 1С такие средства у нас отсутствуют. Очень чисто теоретически, можно покопаться в срезе текущих данных, в известной папке reg_1541, и поискать там нечто похожее в тот момент, когда оно реально крутится. Но если оно окажется NoSQL-хранилищем, то его ещё и распаковывать придётся, и сразу "узнать в лицо" будет трудно...
16. Yashazz 4727 26.08.21 11:55 Сейчас в теме
Добавил примеры, где это вообще имеет смысл. Отформатировал текст, может, так полегче восприниматься будет.
Dmitri93; AnryMc; +2 Ответить
18. AnryMc 849 26.08.21 17:00 Сейчас в теме
(16)
Отформатировал текст, может, так полегче восприниматься будет.


Полегче.

Но в среде 1С - программистов есть (и довольно много) которые не имеют "классического" образования по программированию. И они будут "спотыкаться" при чтении...

З.Ы. Хоть меня когда то и учили программирования, но я "завис" (секунд на 7) буквально в самом начале на "ЯП":
С точки зрения ЯП, это процедуры глобального контекста сеанса


З.Ы.Ы. А так полезно и познавательно.
19. tormozit 7144 26.08.21 17:09 Сейчас в теме
ЯП и СП рекомендую заменить словами.
20. Yashazz 4727 26.08.21 17:51 Сейчас в теме
21. tormozit 7144 26.08.21 18:12 Сейчас в теме
(20) Видимо выборочно сделал?
В СП указано
22. tormozit 7144 28.08.21 08:12 Сейчас в теме
Метод Запустить() в примере должен быть экспортным.
23. Yashazz 4727 29.08.21 09:30 Сейчас в теме
(22) Точно! Исправил, спасибо.
24. Cyberhawk 135 05.09.21 10:53 Сейчас в теме
Реинициализация этой таблицы происходит при любом "возврате на клиент"
А что такое инициализация? Когда и как она происходит?
25. user832106 17.11.21 20:02 Сейчас в теме
"Важно! Запуск и остановка всегда должны находиться в контексте одного модуля, иначе запустить - запустит, но потом не остановит" - а вот за это огромное спасибо! Решил проблему)
26. webresurs 209 03.05.23 09:45 Сейчас в теме
- подскажите как отключить выполнение
&НаКлиенте
Перем ПроверкаОжидания Экспорт;
в расширении ?
Оставьте свое сообщение