Каково время жизни БлокировкиДанных и как реализовать семафор с ожиданием?

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

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

Давайте подробно разберем, как устроены блокировки в 1С, выясним причину возникновения взаимоблокировок и рассмотрим практические подходы к решению этих задач.

Время жизни транзакционной блокировки данных

Для начала развеем главное опасение: блокировка не исчезнет при выходе из области видимости процедуры подписки.

Рассмотрим подробнее механизм работы. В платформе 1С программный объект БлокировкаДанных сам по себе не хранит блокировку. Вызов метода Блокировать() является лишь управляющей командой для Менеджера транзакционных блокировок. Как только этот метод выполняется, блокировка физически фиксируется на уровне Менеджера и жестко привязывается к текущей транзакции (в нашем случае — к системной транзакции записи объекта).

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

Почему важно устанавливать все блокировки одновременно?

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

Выясним причину этого требования. Чаще всего взаимоблокировки происходят из-за эскалации (повышения привилегий) блокировки или из-за нарушения порядка блокирования ресурсов параллельными сеансами. Посмотрим на классический сценарий дедлока:

  1. Транзакция А читает данные, устанавливая разделяемую блокировку.
  2. Транзакция Б также читает эти же данные, устанавливая свою разделяемую блокировку.
  3. Транзакция А пытается изменить данные и запрашивает повышение блокировки до исключительной. Она начинает ждать, пока Транзакция Б снимет свою разделяемую блокировку.
  4. Транзакция Б также пытается изменить данные и тоже запрашивает исключительную блокировку, ожидая Транзакцию А.

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

Пессимистические блокировки и отсутствие очереди ожидания

Теперь рассмотрим ситуацию, когда нам нужна блокировка вне транзакции. Для таких целей в платформе предусмотрена пессимистическая (объектная) блокировка. Ее устанавливают с помощью метода ЗаблокироватьДанныеДляРедактирования.

Эта блокировка действительно умеет жить вне транзакции. Время её жизни определяется двумя факторами:

  1. Привязкой к идентификатору открытой формы на клиенте (снимается при закрытии формы).
  2. Привязкой к текущему сеансу (живет до завершения сеанса или до явного снятия методом РазблокироватьДанныеДляРедактирования).

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

Как реализовать семафор с ожиданием вне транзакции?

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

  1. Цикличный опрос (Spinlock):
    Мы можем сымитировать ожидание программно. Для этого на клиенте можно использовать механизм ОбработкаОжидания, а на сервере — цикл с небольшими искусственными задержками. Внутри цикла помещается конструкция Попытка...Исключение, где мы пробуем выполнить ЗаблокироватьДанныеДляРедактирования. Если выпадает исключение, сеанс «спит» и пробует снова через заданный интервал.
  2. Использование служебной таблицы БД в качестве очереди:
    Создадим выделенный регистр сведений (или константу). Когда сеансу нужен доступ к ресурсу, он начинает свою короткую независимую транзакцию и пытается записать туда служебную запись. Если другой сеанс уже держит этот ресурс, СУБД поставит нашу транзакцию в реальную очередь ожидания на уровне базы данных (до срабатывания таймаута блокировки).
  3. Использование внешних систем:
    В высоконагруженных конфигурациях эмуляция очередей средствами 1С может негативно сказаться на производительности. Разберем современный подход: интеграция с in-memory хранилищами, такими как Redis, или брокерами сообщений (RabbitMQ). Сеанс 1С через HTTP-запросы или внешние компоненты обращается к внешней системе, которая выступает в роли менеджера распределенных блокировок. Внешняя система сама управляет очередями (TTL) и семафорами с ожиданием, возвращая 1С ответ, когда ресурс освободился.

Подводя итог, можно утверждать, что понимание границ жизни БлокировкаДанных спасает от множества неочевидных ошибок, а знание ограничений метода ЗаблокироватьДанныеДляРедактирования позволяет сразу закладывать правильную архитектуру при разработке сложных систем обмена и конкурентного доступа.

← На главную