Почему метод Объект.Заблокировать() не ловится в Попытке и как правильно настроить блокировку данных в 1С 8.3

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

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

Разберем причину: почему Попытка — Исключение не спасает от системного окна

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

Проанализируем также типичное заблуждение, связанное с методом Заблокирован(). Часто разработчики пытаются проверить состояние перед блокировкой:


Если НЕ Объект.Заблокирован() Тогда
    Объект.Заблокировать();
КонецЕсли;

Важный нюанс: метод Объект.Заблокирован() возвращает Истина только в том случае, если данный конкретный экземпляр объекта в памяти текущего сеанса был заблокирован этим же кодом. Он не проверяет базу данных на наличие блокировок, установленных другими пользователями. Именно поэтому проверка всегда выдает Ложь, а последующая попытка блокировки вызывает системное окно.

Решение 1. Использование метода ЗаблокироватьДанныеДляРедактирования()

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

Посмотрим на пример кода, который позволяет избежать системного диалога:


Попытка
    // Параметр ИспользоватьПрикладнуюОшибку = Истина позволяет перехватить ошибку в Исключении
    ЗаблокироватьДанныеДляРедактирования(Объект.Ссылка, , КлючБлокировки, Истина);
Исключение
    Сообщить("Обмен уже запущен другим пользователем или системой!");
    Возврат;
КонецПопытки;

Устанавливая последний параметр в Истина, мы даем платформе указание не выводить стандартное окно с «красным крестом», а передать управление в блок Исключение. Это позволяет нам корректно и мягко сообщить пользователю о ситуации.

Решение 2. Управляемые блокировки — профессиональный подход

Для задач синхронизации серверных процессов, таких как обмен данными, наиболее правильным является использование механизма управляемых блокировок. Они работают внутри транзакции и не привязаны к конкретному экземпляру объекта в памяти.

Выясним, как это реализовать на практике. Мы создаем исключительную блокировку на элемент справочника (например, настройки обмена), которая будет действовать до завершения транзакции:


Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("Справочник.НастройкиОбмена");
ЭлементБлокировки.УстановитьЗначение("Ссылка", НужнаяСсылка);
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;

НачатьТранзакцию();
Попытка
    Блокировка.Заблокировать();
    // Здесь выполняется код обмена данными
    ЗафиксироватьТранзакцию();
Исключение
    ОтменитьТранзакцию();
    Сообщить("Не удалось начать обмен: данные заблокированы другим процессом.");
КонецПопытки;

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

Решение 3. Использование ключа фонового задания (Mutex)

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

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

Пример программного запуска с проверкой:


// Ключ должен быть уникальным для данного вида обмена
КлючЗадания = "ОбменДанными_С_Сайтом_Узел_001";

// Проверяем, не выполняется ли уже такое задание
Фильтр = Новый Структура;
Фильтр.Вставить("Ключ", КлючЗадания);
Фильтр.Вставить("Состояние", СостояниеФоновогоЗадания.Активно);
АктивныеЗадания = ФоновыеЗадания.ПолучитьФоновыеЗадания(Фильтр);

Если АктивныеЗадания.Количество() > 0 Тогда
    Предупреждение("Обмен уже выполняется!");
    Возврат;
КонецЕсли;

// Если заданий нет, запускаем новое
ФоновыеЗадания.Выполнить("МойОбщийМодуль.ВыполнитьОбмен", Параметры, КлючЗадания, "Ручной запуск обмена");

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

Выводы и рекомендации

Подводя итог, можно выделить следующие правила выбора инструмента блокировки:

  1. Для интерфейсных форм: используем ЗаблокироватьДанныеДляРедактирования с флагом использования прикладной ошибки.
  2. Для серверного кода и транзакций: применяем БлокировкаДанных (управляемые блокировки) — это стандарт современной разработки.
  3. Для синхронизации длительных процессов: лучше всего подходят Ключи фоновых заданий, так как они работают на уровне всей системы и не зависят от состояния конкретных записей в таблицах БД.

Помните, что использование метода Объект.Заблокировать() в современных конфигурациях на управляемых формах считается устаревшим подходом, который часто ведет к трудноуловимым ошибкам в клиент-серверной среде.

← На главную