Для реализации будем использовать:
1. Механизм управляемых блокировок
2. Механизм фоновых заданий и их метод: "ОжидатьЗавершенияВыполнения"
Смысл данной реализации схематично можно представить следующим образом:
Теперь к реализации:
1. Решение состоит из общего модуля и регистра сведений. (+ тестовая обработка для демонстрации работы паузы)
2. Регистр сведений необходим для реализации блокировки, по определенному условию. В нашем случае, это "НомерСеанса" (Число). Так как в одну единицу времени у информационной базы не может быть два сеанса с одинаковым номером, что позволяет нам утверждать, что пересечений по блокировкам не будет. Другими словами - одна пауза не помешает другому сеансу установить свою паузу.
3. Процедуры и функции
Процедура ВыполнитьПаузу_вСекундах(ВремяПуаузыСек,Причина="")
Параметры:
ВремяПаузыСек - Число - Количество секунд, на которое необходимо установить паузу
Причина - Строка - произвольный текст, будут установлен в запись регистра сведений
Текст с пояснениями:
Процедура ВыполнитьПаузу_ВСекундах(ВремяПаузыСек,Причина="") Экспорт
пНомерСеанса = НомерСеансаИнформационнойБазы(); // Номер текущего сеанса
пИмяИнициатора = ИмяПользователя(); // имя пользователя
// Создаем запись о паузе в регистре сведений
МЗ = РегистрыСведений.apПауза.СоздатьМенеджерЗаписи();
МЗ.НомерСеанса = пНомерСеанса;
МЗ.ДатаНачалаПаузы = ТекущаяУниверсальнаяДата();
МЗ.ДатаОкончанияПаузы = МЗ.ДатаНачалаПаузы + ВремяПаузыСек + 3; // Необходимо значение для сборщика зависших пауз (+3 секунды на всякий случай "оверхеда")
МЗ.Причина = Причина;
МЗ.ИмяИнициатора = пИмяИнициатора;
МЗ.Записать();
НачатьТранзакцию(); // Вне транзакций блокировка невозможна
Блокировка = новый БлокировкаДанных;
Элементблокировки = Блокировка.Добавить("Регистрсведений.apПауза");
Элементблокировки.УстановитьЗначение("НомерСеанса",пНомерСеанса);
Элементблокировки.Режим=РежимБлокировкиДанных.Исключительный;
Блокировка.Заблокировать(); // Установили блокировку (ни один другой сеанс не может прочесть это значение)
МассивПараметров = Новый Массив;
МассивПараметров.Добавить(пНомерСеанса);
МассивПараметров.Добавить(МЗ.ДатаОкончанияПаузы + ВремяПаузыСек);
//Запускаем фоновое задание, передаем параметр текущего номера сеанса
ФЗ = ФоновыеЗадания.Выполнить("apПауза.сис_Пауза",МассивПараметров,,"пауза от " + пИмяИнициатора + " (" + пНомерСеанса + ")");
Если РежимСовместимости_8_13_ИВыше() Тогда // у платформы с 8.3.13 "ОжидатьЗавершенияВыполнения", более ранняя "ОжидатьЗавершения"
Попытка
ФЗ = ФЗ.ОжидатьЗавершенияВыполнения(ВремяПаузыСек); // Время ожидания равно времени паузы
Исключение;
КонецПопытки;
Иначе
Попытка
ФЗ.ОжидатьЗавершения(ВремяПаузыСек); // Время ожидания равно времени паузы
Исключение;
КонецПопытки;
КонецесЛИ;
ОтменитьТранзакцию(); // пауза закончена, снимем блокировку
МЗ.НомерСеанса = пНомерСеанса;
МЗ.Удалить(); // Удалим запись о паузе
КонецПроцедуры
Процедура сис_Пауза(НомерСеанса,ВремяОкончанияПаузы)
Параметры:
НомерСеанса - Число - Номер сеанса, который ставится на паузу
ВремяОкончанияПаузы- Дата в UTC- Когда должна закончится пауза (когда пауза больше времени ожидания на снятие блокировки)
Текст с пояснениями:
Процедура сис_Пауза(НомерСеанса,ВремяОкончанияПаузы) Экспорт
МЗ = РегистрыСведений.apПауза.СоздатьМенеджерЗаписи();
МЗ.НомерСеанса = НомерСеанса;
Пока ТекущаяУниверсальнаяДата() < ВремяОкончанияПаузы Цикл // Для пуаз больше 20 сек (Таймаута ожидания запроса по умолчанию)
Попытка;
МЗ.Прочитать(); // попытка чтения заблокированной запись - здесь и возникает ожидание паузы
Исключение;
КонецПопытки;
КонецЦикла;
КонецПроцедуры
Процедура сборщика выполняется регламентным заданием и удаляет неактуальные записи, которые могли остаться в регистре сведений (не критично, будет работать и без регулярной чистки)
Функция получения режима совместимости и версии платформы, необходима для определения какой из методов ожидания завершения использовать у фонового задания
Процедура СборщикЗависшихПауз() Экспорт
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| apПауза.НомерСеанса КАК НомерСеанса
|ИЗ
| РегистрСведений.apПауза КАК apПауза
|ГДЕ
| apПауза.ДатаОкончанияПаузы < &ТекущаяУниверсальнаядата";
запрос.УстановитьПараметр("ТекущаяУниверсальнаядата",ТекущаяУниверсальнаяДата());
Выборка = запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
МЗ = РегистрыСведений.Пауза.СоздатьМенеджерЗаписи();
МЗ.НомерСеанса = Выборка.НомерСеанса;
МЗ.Удалить();
КонецЦикла;
конецПроцедуры
Функция РежимСовместимости_8_13_ИВыше() Экспорт
ТекРежим = Метаданные.РежимСовместимости;
Если ТекРежим = Метаданные.СвойстваОбъектов.РежимСовместимости.НеИспользовать тогда
пСис = Новый СистемнаяИнформация;
пВерсия = пСис.ВерсияПриложения;
пВерсия = СтрЗаменить(пВерсия,".",Символы.ПС);
пПерваяВерсия = Число(СтрПолучитьСтроку(пВерсия,1));
пВтораяВерсия = Число(СтрПолучитьСтроку(пВерсия,2));
пТретьяВерсия = Число(СтрПолучитьСтроку(пВерсия,3));
Иначе
пВерсия = СтрЗаменить(СтрЗаменить(Строка(ТекРежим),"Версия",""),"_",Символы.ПС);
пПерваяВерсия = Число(СтрПолучитьСтроку(пВерсия,1));
пВтораяВерсия = Число(СтрПолучитьСтроку(пВерсия,2));
пТретьяВерсия = Число(СтрПолучитьСтроку(пВерсия,3));
КонецЕсли;
Если пТретьяВерсия <= 12 И пВтораяВерсия <= 3 И пПерваяВерсия = 8 Тогда
Возврат Ложь;
Иначе
Возврат Истина;
КонецЕсли;
КонецФункции
Тест паузы, как видно, в реальности пауза устанавливается на больше, чем указано в значении, примерно на 0.03 секунды.
Эти 30 мил.сек. как раз затраты на реализацию и обслуживание выполнения паузы.
Текст модуля формы обработки Теста паузы
&НаСервере
Процедура ВстатьНаПаузуНаСервере()
пТекДата = ТекущаяДата();
мНачало = ТекущаяУниверсальнаяДатаВМиллисекундах();
apПауза.ВыполнитьПаузу_ВСекундах(ПаузавСекундах,"Тестовая пауза");
Результаты.ДобавитьСтроку(Формат(пТекДата,"ДФ='dd.MM.yy HH:mm:ss'") + " пауза на " + Строка(ПаузавСекундах) +" сек. выполнена за " + Формат(ТекущаяУниверсальнаяДатаВМиллисекундах()-мНачало,"ЧГ=0") + " мил.сек.");
КонецПроцедуры
&НаКлиенте
Процедура ВстатьНаПаузу(Команда)
ВстатьНаПаузуНаСервере();
КонецПроцедуры
Теперь в любом месте конфигурации можно написать "apПауза.ВыполнитьПаузу_ВСекундах(5);"
И система встанет в ожидание на 5 секунд, при этом будет работать на любой платформе, без использования внешних компонент и большого потребления ресурсов сервера.
На картинке видно ожидание на управляемой блокировке, что фоновое задание, ждет, когда Тонкий клиент "отпустит" ресурс.
... Протестировано на нагрузке в 50 и более сеансов.
тестировалось на платформах 8.3.12, 8.3.15 без режима совместимости и в режиме совместимости 8.3.12
Для примера приведу рабочий кейс использования такой паузы:
Дано: система расчета показателей Apdex в режиме условно реального времени. По алгоритму для расчета индекса APDEX, необходимо знать время выполнения последних 100 таких же ключевых операций. Одна и та же ключевая операция может быть выполнена одновременно несколькими пользователями.
Решение:
1. Есть API, которая регистрирует выполнение ключевой операции в регистре сведений,
2. Регламентное задание, с периодичностью 1 раз в 60 сек стартует фоновые задания с анализом по каждому виду ключевых операции
3. Анализ по ключевой операции заключается в следующем:
Получение последних 100 записей из регистра сведений по данной ключевой операции
Создание таблицы значений с этими операциями
замещение новыми операциями, если есть (получение новых операций)
Подсчет итога по таблице
Установка паузы в 3 секунды и далее получение новых операций, без запроса к старым
Если нового замера по ключевой операции не поступало в течении 30 минут, то фоновое задание завершается
Такая реализация позволила убрать постоянный запрос к регистру с историей операций (более 10 млн. записей)
...
В файлах можно скачать расширение или конфигурацию, в которой реализован описанный выше механизм паузы.
...
А зачем вам нужна пауза в 1С?