Проанализируем довольно частую ситуацию, с которой сталкиваются разработчики платформы 1С:Предприятие при работе с блокировками. Допустим, у нас есть подписка на событие «Перед записью документа». В этой подписке (которая располагается в общем модуле) нам нужно программно заблокировать некий регистр, а затем изменить некоторые данные в объекте. Возникает закономерный вопрос: не разрушится ли установленная блокировка после выхода из процедуры подписки, ведь локальная переменная объекта БлокировкаДанных уничтожается? И как вообще реализовать блокировку, которая могла бы работать вне системной транзакции и умела бы «ждать»?
Давайте подробно разберем, как устроены блокировки в 1С, выясним причину возникновения взаимоблокировок и рассмотрим практические подходы к решению этих задач.
Для начала развеем главное опасение: блокировка не исчезнет при выходе из области видимости процедуры подписки.
Рассмотрим подробнее механизм работы. В платформе 1С программный объект БлокировкаДанных сам по себе не хранит блокировку. Вызов метода Блокировать() является лишь управляющей командой для Менеджера транзакционных блокировок. Как только этот метод выполняется, блокировка физически фиксируется на уровне Менеджера и жестко привязывается к текущей транзакции (в нашем случае — к системной транзакции записи объекта).
Даже если программная переменная уничтожается сборщиком мусора, сама установленная блокировка будет жить вплоть до окончания транзакции (до момента ее успешной фиксации или отката). Таким образом, управляемые блокировки всегда существуют ровно до конца транзакции, в рамках которой они были вызваны.
Проанализируем еще один важный нюанс, затронутый в обсуждении — взаимоблокировки (deadlocks). Правила хорошего тона и стандарты разработки 1С гласят, что все необходимые блокировки нужно устанавливать одновременно в самом начале транзакции.
Выясним причину этого требования. Чаще всего взаимоблокировки происходят из-за эскалации (повышения привилегий) блокировки или из-за нарушения порядка блокирования ресурсов параллельными сеансами. Посмотрим на классический сценарий дедлока:
Результат — бесконечное ожидание и принудительный обрыв одной из транзакций системой. Чтобы избежать этого, заранее формируйте единый массив пространств блокировок и вызывайте метод Блокировать() один раз для всех нужных ресурсов. Если ресурсы блокируются в разных местах кода, всегда соблюдайте строгий хронологический или алфавитный порядок блокирования.
Теперь рассмотрим ситуацию, когда нам нужна блокировка вне транзакции. Для таких целей в платформе предусмотрена пессимистическая (объектная) блокировка. Ее устанавливают с помощью метода ЗаблокироватьДанныеДляРедактирования.
Эта блокировка действительно умеет жить вне транзакции. Время её жизни определяется двумя факторами:
РазблокироватьДанныеДляРедактирования).Однако у этого механизма есть существенный недостаток для решения задачи классического системного семафора: она без ожидания. Платформа 1С не имеет встроенных механизмов постановки сеанса в очередь ожидания для пессимистических блокировок. Если данные уже заблокированы другим сеансом, при попытке блокировки немедленно сгенерируется исключение.
Поскольку встроенного семафора с очередью ожидания в 1С нет, разберем по шагам рабочие архитектурные альтернативы, которые применяют разработчики на практике:
ОбработкаОжидания, а на сервере — цикл с небольшими искусственными задержками. Внутри цикла помещается конструкция Попытка...Исключение, где мы пробуем выполнить ЗаблокироватьДанныеДляРедактирования. Если выпадает исключение, сеанс «спит» и пробует снова через заданный интервал.Подводя итог, можно утверждать, что понимание границ жизни БлокировкаДанных спасает от множества неочевидных ошибок, а знание ограничений метода ЗаблокироватьДанныеДляРедактирования позволяет сразу закладывать правильную архитектуру при разработке сложных систем обмена и конкурентного доступа.