Почему повторный вызов ЗаблокироватьДанныеДляРедактирования() не снимает блокировку и как это исправить?

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

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

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

Анализ проблемного сценария

Рассмотрим по шагам алгоритм, который приводит к зависанию блокировки в системе:

  1. Сеанс 1: Пользователь открывает форму документа. При открытии программно вызывается метод ЗаблокироватьДанныеДляРедактирования(Ссылка, , ИдентификаторФормы). Объект успешно блокируется для редактирования другими пользователями.
  2. Сеанс 1: Производится запись документа в базу данных. В этот момент версия данных объекта меняется на физическом уровне базы данных.
  3. Сеанс 1: Сразу после записи (вне транзакции) повторно вызывается метод ЗаблокироватьДанныеДляРедактирования(Ссылка, , ИдентификаторФормы) с целью продлить или подтвердить удержание блокировки в открытой форме.
  4. Сеанс 1: При закрытии формы или по действию пользователя вызывается метод РазблокироватьДанныеДляРедактирования(Ссылка, ИдентификаторФормы).
  5. Сеанс 2: Другой пользователь пытается открыть эту же карточку документа, при этом его сеанс выполняет вызов ЗаблокироватьДанныеДляРедактирования(Ссылка, , ИдентификаторФормыСеанса2). Платформа выбрасывает исключение о том, что объект заблокирован первым сеансом.

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

Причина 1: Рекурсивный счетчик (захват) объектных блокировок

Пессимистические объектные блокировки в «1С:Предприятии 8» спроектированы по принципу рекурсивного счетчика (аналогично механизму reentrant lock в классическом многопоточном программировании). Это означает следующее:

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

Соответственно, однократный вызов метода РазблокироватьДанныеДляРедактирования() лишь уменьшает этот внутренний счетчик на единицу, но не удаляет запись о блокировке из менеджера блокировок службы 1С полностью, так как счетчик все еще больше нуля. Чтобы полностью освободить ресурс, требуется вызвать разблокировку ровно столько раз, сколько раз была установлена блокировка.

Причина 2: Изменение версии данных (ВерсияДанных) при записи

Каждая запись объекта в базу данных приводит к изменению системного реквизита ВерсияДанных (в СУБД это обычно реализуется через тип данных rowversion или timestamp). Смена версии кардинально меняет поведение менеджера блокировок:

В некоторых релизах платформы служба блокировок сервера 1С регистрирует это как два абсолютно разных ключа блокировки (блокировку устаревшей версии V1 и блокировку новой версии V2). При вызове стандартного метода разблокировки без указания конкретной версии платформа может некорректно сопоставить ключи в памяти, из-за чего блокировка версии V1 зависает в памяти сервера приложений как «осиротевшая».

Причина 3: Несовместимость транзакционных и объектных блокировок

При выполнении записи объекта внутри транзакции СУБД и сама платформа накладывают собственные системные транзакционные блокировки. Если в этот момент на объекте уже висит активная ручная пессимистическая блокировка с привязкой к конкретному идентификатору формы, менеджер блокировок может войти в конфликтное состояние. Это препятствует корректному автоматическому или ручному очищению зарегистрированных ключей блокировки после завершения транзакции.


Способы решения проблемы

Разберем три практических способа обхода и исправления данной проблемы в кодовой базе.

Решение 1: Паттерн «Сброс-Захват» (Рекомендуемый и самый надежный способ)

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


// 1. Установка блокировки при открытии формы
ЗаблокироватьДанныеДляРедактирования(СсылкаНаДокумент, , ИдентификаторФормы);

// ... Производятся изменения данных пользователем ...

// 2. Перед записью или непосредственно после записи сбрасываем старую блокировку
РазблокироватьДанныеДляРедактирования(СсылкаНаДокумент, ИдентификаторФормы);

// 3. Производим запись объекта в базу данных
ДокументОбъект.Записать();

// 4. Устанавливаем блокировку заново для продолжения редактирования в форме
ЗаблокироватьДанныеДляРедактирования(СсылкаНаДокумент, , ИдентификаторФормы);

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

Решение 2: Использование второго параметра «ВерсияДанных»

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


// При открытии формы получаем текущую версию данных из ссылки
ТекущаяВерсия = ОбщегоНазначения.ВерсияДанныхОбъекта(СсылкаНаДокумент); 
ЗаблокироватьДанныеДляРедактирования(СсылкаНаДокумент, ТекущаяВерсия, ИдентификаторФормы);

// Выполняем перезапись объекта в коде
ДокументОбъект = СсылкаНаДокумент.ПолучитьОбъект();
// ... изменение реквизитов ...
ДокументОбъект.Записать();

// Получаем новую версию данных после успешной записи документа
НоваяВерсия = ДокументОбъект.ВерсияДанных;

// Блокируем объект под новой версией данных
ЗаблокироватьДанныеДляРедактирования(СсылкаНаДокумент, НоваяВерсия, ИдентификаторФормы);

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

Решение 3: Переход на использование блокировок управляемой формы

Если вы работаете в управляемом интерфейсе, ручное управление пессимистическими блокировками через ЗаблокироватьДанныеДляРедактирования() часто избыточно. Платформа предоставляет более высокоуровневый и безопасный механизм блокирования данных формы. Рассмотрим использование методов ЗаблокироватьДанныеФормыДляРедактирования() и РазблокироватьДанныеФормыДляРедактирования().

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

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


&НаСервере
Процедура ЗаблокироватьФормуНаСервере()
    // Блокируем данные формы, привязанной к объекту
    Попытка
        ЗаблокироватьДанныеФормыДляРедактирования();
    Исключение
        ВызватьИсключение "Не удалось заблокировать документ для редактирования. " + ОписаниеОшибки();
    КонецПопытки;
КонецПроцедуры

&НаСервере
Процедура РазблокироватьФормуНаСервере()
    РазблокироватьДанныеФормыДляРедактирования();
КонецПроцедуры

Резюме и рекомендации

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

← На главную