Осторожнее с бесконечными циклами

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

Клиент обратился с проблемой, что 1С зависает при запуске.

Я посмотрел и обнаружил проблемный бесконечный цикл, за время, пока я ужинал, он накрутил 2 миллиона повторений:

Удручает, что такое встречается в отраслевом коде тиражных решений. Называть не буду, чтобы лишний раз не пристыжать разработчиков.

Вот код, уже обработанный мною:


//Осипов - контроль чтобы повторно не срабатывало.
ТаблицаПериодовКонтроля.Колонки.Добавить("Обработано", Новый ОписаниеТипов("Булево"));
ТаблицаПериодовКонтроля.Колонки.Добавить("Номер", Новый ОписаниеТипов("Число"));
Сч = 1;
Для Каждого Строка ИЗ ТаблицаПериодовКонтроля Цикл
	Строка.Номер = Сч;
	Сч = Сч + 1;
КонецЦикла;

// Заполним временную таблицу.
ЗапросПроверкиДокумента.УстановитьПараметр("ТаблицаПериодовКонтроля", ТаблицаПериодовКонтроля);
ЗапросПроверкиДокумента.МенеджерВременныхТаблиц = МенеджерВТ;

ТекстЗапросаТаблицыПериодовКонтроля = 
"ВЫБРАТЬ
|	ТаблицаПериодовКонтроля.Период,
|	ТаблицаПериодовКонтроля.Организация,
|	ТаблицаПериодовКонтроля.Номер,
|	ТаблицаПериодовКонтроля.Обработано,
|	ТаблицаПериодовКонтроля.Филиал
|ПОМЕСТИТЬ ТаблицаПериодовКонтроля
|ИЗ
|	&ТаблицаПериодовКонтроля КАК ТаблицаПериодовКонтроля";
ЗапросПроверкиДокумента.Текст = ТекстЗапросаТаблицыПериодовКонтроля;
ЗапросПроверкиДокумента.Выполнить();


// Сформируем запрос для выполнения в цикле.
ТекстЗапросаТекущейТаблицыПериодовКонтроля = 
"ВЫБРАТЬ РАЗРЕШЕННЫЕ
|	ТаблицаПериодовКонтроля.Организация,
|	ТаблицаПериодовКонтроля.Филиал,
|	ТаблицаПериодовКонтроля.Номер,
|	ТаблицаПериодовКонтроля.Период
|ИЗ
|	ТаблицаПериодовКонтроля КАК ТаблицаПериодовКонтроля
|		ЛЕВОЕ СОЕДИНЕНИЕ Документ.Х КАК Х
|		ПО ТаблицаПериодовКонтроля.Организация = Х.Организация
|			И ТаблицаПериодовКонтроля.Филиал = Х.Филиал
|			И (ТаблицаПериодовКонтроля.Период = НАЧАЛОПЕРИОДА(Х.Дата, ДЕНЬ))
|			И (Х.Проведен)
|ГДЕ
|	Х.Ссылка ЕСТЬ NULL И НЕ ТаблицаПериодовКонтроля.Обработано";
ЗапросПроверкиДокумента.Текст = ТекстЗапросаТекущейТаблицыПериодовКонтроля;

// Служебные переменные для информирования о формировании документов.
КоличествоСформированныхДокументов = 0;
КоличествоСформированныхДокументовСОшибками = 0;

Пока Истина Цикл
	
	// Формируем необходимые документы.
	// Выполнение запроса происходит после обработки каждого документа - на случай,
	// если за время обработки документа уже началось формирование другого.
	РезультатЗапроса = ЗапросПроверкиДокумента.Выполнить();
	ВыборкаПараметров = РезультатЗапроса.Выбрать();
	Если Не ВыборкаПараметров.Следующий() Тогда
		Прервать;
	КонецЕсли;
	
	// Нужного документа нет, необходимо создать.
	ДокументОбъект = Документы.Х.СоздатьДокумент();
	
	// Заполняем шапку.
	ДокументОбъект.Дата        = ВыборкаПараметров.Период;
	ДокументОбъект.Организация = ВыборкаПараметров.Организация;
	ДокументОбъект.Филиал      = ВыборкаПараметров.Филиал;
	
	// Сразу записываем, чтобы "забронировать" место до расчета, поскольку
	// он может быть продолжительным.
	ДокументОбъект.Проведен = Истина;
	ЗаписатьОбъект(ДокументОбъект, РежимЗаписиДокумента.Запись);
	ДокументОбъект.Заблокировать();
	
	//Заполнение шапки и таблицы документа
	...
	
	
	//Осипов - помечаем что обработали
	ТаблицаПериодовКонтроля[ВыборкаПараметров.Номер - 1].Обработано = Истина;
	
	// Проводим документ.
	Попытка
		ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);
		МассивСообщенийПользователю.Добавить("Сформирован документ Х №" + ДокументОбъект.Номер + " от " + ДокументОбъект.Дата + ".");
	Исключение
		МассивСообщенийПользователю.Добавить("Не удалось провести документ Х №" + ДокументОбъект.Номер + " от " + ДокументОбъект.Дата + ": " + ОписаниеОшибки());
		КоличествоСформированныхДокументовСОшибками = КоличествоСформированныхДокументовСОшибками + 1;
	КонецПопытки;
	
	КоличествоСформированныхДокументов = КоличествоСформированныхДокументов + 1;
	
	//Следующая итерация - Осипов
	ЗапросПроверкиДокумента.Текст = "УНИЧТОЖИТЬ ТаблицаПериодовКонтроля; " + ТекстЗапросаТаблицыПериодовКонтроля;
	ЗапросПроверкиДокумента.Выполнить();
	ЗапросПроверкиДокумента.Текст = ТекстЗапросаТекущейТаблицыПериодовКонтроля;
	
КонецЦикла;

// Вывод сводной информации по документам.
Если КоличествоСформированныхДокументов > 0 Тогда
	МассивСообщенийПользователю.Добавить("Всего сформировано документов - " + КоличествоСформированныхДокументов + ", из них с ошибками проведения - " + КоличествоСформированныхДокументовСОшибками + ".");
	МассивСообщенийПользователю.Добавить("");
КонецЕсли;

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

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

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

После этого цикл имеет гарантированный конечный объем.

Объем: 1 час.

fixin

Программирую на 1С с 1999 года. В 1С просто Гений. В 2020 году ушел из офиса на вольные хлеба фриланса. Принимаю заказы.

Читайте также:

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *