Как работает асинхронность в клиенте 1С:Предприятие: разбираемся в механизмах событийного цикла

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

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

Понятие асинхронности vs многопоточности в 1С

Для начала разграничим два критически важных понятия. Многопоточность (Multithreading) предполагает одновременное выполнение нескольких участков кода в разных потоках операционной системы. Асинхронность в контексте клиента 1С — это событийная модель управления, при которой выполнение задачи может быть отложено, а управление возвращено системе для обработки других событий.

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

Механизм Event Loop (Событийный цикл)

Рассмотрим, как работает движок клиента 1С. Его логика работы крайне схожа с механизмом Event Loop в браузерах (JavaScript). Работает это по следующей схеме:

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

Именно поэтому, если вы напишете бесконечный цикл или очень тяжелое вычисление непосредственно в коде формы, интерфейс «зависнет». Платформа просто не сможет дойти до обработки события «Отрисовать окно» или «Нажатие кнопки», пока ваш код не завершится.

Работа с обработчиками ожидания

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

Разберем по шагам, как это выполнит платформа:

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

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


&НаКлиенте
Процедура УвеличитьСчетчик1()
    Счетчик = Счетчик + 1;
КонецПроцедуры

&НаКлиенте
Процедура УвеличитьСчетчик2()
    Счетчик = Счетчик + 1;
КонецПроцедуры

Даже если системные прерывания для УвеличитьСчетчик1 и УвеличитьСчетчик2 придут одновременно, выполнение будет строго последовательным. В настоящей многопоточной среде (например, на C++ или Java без синхронизации) могла бы возникнуть ситуация "Race Condition": обе процедуры одновременно прочитали бы значение Счетчик (пусть это 5), прибавили бы 1 и обе записали бы 6. В итоге вместо 7 мы получили бы 6. В 1С это невозможно: вторая процедура начнет чтение переменной только после того, как первая полностью завершит операцию записи и выйдет из процедуры.

Низкий приоритет обработчиков ожидания

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

Новая модель асинхронности: Асинх и Ждать

Начиная с версии 8.3.18, в платформе появились ключевые слова Асинх (Async) и Ждать (Await). Это развитие механизма ОписаниеОповещения, которое делает код более читаемым. Рассмотрим, меняет ли это суть однопоточности.

Когда выполнение доходит до оператора Ждать, происходит следующее:

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

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

Взаимодействие с внешними компонентами

Единственный случай, когда в клиенте 1С появляется реальное параллельное выполнение — это работа Внешних компонент (Native API). Компонента может создать собственные потоки в операционной системе. Например, компонента для работы с торговым оборудованием может в отдельном потоке постоянно опрашивать порт весов.

Однако, даже здесь сохраняется безопасность данных 1С. Внешняя компонента не имеет прямого доступа к памяти переменных встроенного языка. Чтобы передать данные обратно в 1С, она генерирует ВнешнееСобытие. Это событие попадает в ту же самую общую очередь Event Loop и обрабатывается процедурой ВнешнееСобытиеПрогона (или аналогичной) строго последовательно.

Подведем итоги

Проанализировав работу клиента 1С, мы можем сделать следующие выводы:

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

← На главную