Как проверить, занят ли объект 1С, и избежать конфликтов при параллельной обработке?

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

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

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

Использование объектного метода Заблокирован()

Первое и самое простое решение, которое приходит на ум — это использование встроенного метода Заблокирован(). Рассмотрим ситуацию, когда мы перебираем документы и хотим понять, не открыты ли они у кого-то другого или не обрабатываются ли другим сеансом. Метод Заблокирован() возвращает булево значение, указывающее, установлена ли на объект объектная блокировка.

Проанализируем пример логики, который часто применяют начинающие разработчики:


Для Каждого ТекДокумент Из СписокДокументов Цикл
    Объект = ТекДокумент.ПолучитьОбъект();
    Если Объект.Заблокирован() Тогда
        Продолжить; // Объект занят, пропускаем
    Иначе
        Объект.Заблокировать();
        // Выполняем действия с объектом
    КонецЕсли;
КонецЦикла;

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

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

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

Разберем, чем этот метод лучше:

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

Метод «Попытка — Исключение» при захвате объекта

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


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

Этот подход исключает «состояние гонки», так как попытка захвата атомарна. Если Заблокировать() не сработал, вы точно знаете, что объект обрабатывать сейчас нельзя.

Управляемые блокировки (DataLock)

В современных конфигурациях на платформе 8.3, работающих в режиме управляемых блокировок, наиболее правильным методом является использование объекта БлокировкаДанных. Это позволяет заблокировать данные на уровне сервера 1С еще до того, как начнется транзакция записи.

Выясним, как это реализовать программно:


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

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

Использование БлокировкаДанных является стандартом для высоконагруженных систем, так как оно позволяет минимизировать конфликты на уровне СУБД и четко управлять очередью доступа к данным.

Предотвращение повторного запуска регламентного задания

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


Отбор = Новый Структура;
Отбор.Вставить("ИмяМетаданных", "РегламентноеЗадание.ОбработкаНепроведенных");
Отбор.Вставить("Состояние", СостояниеФоновогоЗадания.Активно);

АктивныеЗадания = ФоновыеЗадания.ПолучитьФоновыеЗадания(Отбор);

Если АктивныеЗадания.Количество() > 1 Тогда
    // Одно задание уже работает, второе должно завершиться
    Возврат;
КонецЕсли;

Проанализировав ситуацию, можно сделать вывод: проверка через ФоновыеЗадания защищает «периметр» задачи, а использование Попытка — Исключение в связке с Заблокировать() или БлокировкаДанных гарантирует сохранность данных на уровне конкретных записей.

Рекомендации по выбору метода

Выясним, какой метод в каком случае использовать:

  1. Для простых обработок в обычном приложении подойдет Объект.Заблокировать() внутри блока Попытка.
  2. Для серверных регламентных заданий в управляемом приложении используйте БлокировкаДанных.
  3. Если нужно визуальное оповещение пользователя в форме, используйте Заблокирован(), но не полагайтесь на него при записи данных.
  4. В высоконагруженных системах можно добавить в документ реквизит В_Обработке (Булево) и менять его в короткой транзакции перед началом длительных расчетов.

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

← На главную