Каталог решений - Пауза на платформе 1С, очень нативная и очень бережная к ресурсам

Пауза на платформе 1С, очень нативная и очень бережная к ресурсам

Пауза на платформе 1С, очень нативная и очень бережная к ресурсам

В наличии

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

Категория:

Описание

Для реализации будем использовать:

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С?

has been added to your cart:
Оформление заказа