Осторожнее с бесконечными циклами
Нет абсолютно плохих или абсолютно хороших вещей. Нож может спасать жизни в руках хирурга и отнимать жизнь в руках маньяка. Так и с бесконечными циклами — иногда они работают, как задумано, иногда приводят к зацикливанию.
Клиент обратился с проблемой, что 1С зависает при запуске.
Я посмотрел и обнаружил проблемный бесконечный цикл, за время, пока я ужинал, он накрутил 2 миллиона повторений:
Удручает, что такое встречается в отраслевом коде тиражных решений. Называть не буду, чтобы лишний раз не пристыжать разработчиков.
Вот код, уже обработанный мною:
//Осипов - контроль чтобы повторно не срабатывало. ТаблицаПериодовКонтроля.Колонки.Добавить("Обработано", Новый ОписаниеТипов("Булево")); ТаблицаПериодовКонтроля.Колонки.Добавить("Номер", Новый ОписаниеТипов("Число")); Сч = 1; Для Каждого Строка ИЗ ТаблицаПериодовКонтроля Цикл Строка.Номер = Сч; Сч = Сч + 1; КонецЦикла; // Заполним временную таблицу. ЗапросПроверкиДокумента.УстановитьПараметр("ТаблицаПериодовКонтроля", ТаблицаПериодовКонтроля); ЗапросПроверкиДокумента.МенеджерВременныхТаблиц = МенеджерВТ; ТекстЗапросаТаблицыПериодовКонтроля = "ВЫБРАТЬ | ТаблицаПериодовКонтроля.Период, | ТаблицаПериодовКонтроля.Организация, | ТаблицаПериодовКонтроля.Номер, | ТаблицаПериодовКонтроля.Обработано, | ТаблицаПериодовКонтроля.Филиал |ПОМЕСТИТЬ ТаблицаПериодовКонтроля |ИЗ | &ТаблицаПериодовКонтроля КАК ТаблицаПериодовКонтроля"; ЗапросПроверкиДокумента.Текст = ТекстЗапросаТаблицыПериодовКонтроля; ЗапросПроверкиДокумента.Выполнить(); // Сформируем запрос для выполнения в цикле. ТекстЗапросаТекущейТаблицыПериодовКонтроля = "ВЫБРАТЬ РАЗРЕШЕННЫЕ | ТаблицаПериодовКонтроля.Организация, | ТаблицаПериодовКонтроля.Филиал, | ТаблицаПериодовКонтроля.Номер, | ТаблицаПериодовКонтроля.Период |ИЗ | ТаблицаПериодовКонтроля КАК ТаблицаПериодовКонтроля | ЛЕВОЕ СОЕДИНЕНИЕ Документ.Х КАК Х | ПО ТаблицаПериодовКонтроля.Организация = Х.Организация | И ТаблицаПериодовКонтроля.Филиал = Х.Филиал | И (ТаблицаПериодовКонтроля.Период = НАЧАЛОПЕРИОДА(Х.Дата, ДЕНЬ)) | И (Х.Проведен) |ГДЕ | Х.Ссылка ЕСТЬ NULL И НЕ ТаблицаПериодовКонтроля.Обработано"; ЗапросПроверкиДокумента.Текст = ТекстЗапросаТекущейТаблицыПериодовКонтроля; // Служебные переменные для информирования о формировании документов. КоличествоСформированныхДокументов = 0; КоличествоСформированныхДокументовСОшибками = 0; Пока Истина Цикл // Формируем необходимые документы. // Выполнение запроса происходит после обработки каждого документа - на случай, // если за время обработки документа уже началось формирование другого. РезультатЗапроса = ЗапросПроверкиДокумента.Выполнить(); ВыборкаПараметров = РезультатЗапроса.Выбрать(); Если Не ВыборкаПараметров.Следующий() Тогда Прервать; КонецЕсли; // Нужного документа нет, необходимо создать. ДокументОбъект = Документы.Х.СоздатьДокумент(); // Заполняем шапку. ДокументОбъект.Дата = ВыборкаПараметров.Период; ДокументОбъект.Организация = ВыборкаПараметров.Организация; ДокументОбъект.Филиал = ВыборкаПараметров.Филиал; // Сразу записываем, чтобы "забронировать" место до расчета, поскольку // он может быть продолжительным. ДокументОбъект.Проведен = Истина; ЗаписатьОбъект(ДокументОбъект, РежимЗаписиДокумента.Запись); ДокументОбъект.Заблокировать(); //Заполнение шапки и таблицы документа ... //Осипов - помечаем что обработали ТаблицаПериодовКонтроля[ВыборкаПараметров.Номер - 1].Обработано = Истина; // Проводим документ. Попытка ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение); МассивСообщенийПользователю.Добавить("Сформирован документ Х №" + ДокументОбъект.Номер + " от " + ДокументОбъект.Дата + "."); Исключение МассивСообщенийПользователю.Добавить("Не удалось провести документ Х №" + ДокументОбъект.Номер + " от " + ДокументОбъект.Дата + ": " + ОписаниеОшибки()); КоличествоСформированныхДокументовСОшибками = КоличествоСформированныхДокументовСОшибками + 1; КонецПопытки; КоличествоСформированныхДокументов = КоличествоСформированныхДокументов + 1; //Следующая итерация - Осипов ЗапросПроверкиДокумента.Текст = "УНИЧТОЖИТЬ ТаблицаПериодовКонтроля; " + ТекстЗапросаТаблицыПериодовКонтроля; ЗапросПроверкиДокумента.Выполнить(); ЗапросПроверкиДокумента.Текст = ТекстЗапросаТекущейТаблицыПериодовКонтроля; КонецЦикла; // Вывод сводной информации по документам. Если КоличествоСформированныхДокументов > 0 Тогда МассивСообщенийПользователю.Добавить("Всего сформировано документов - " + КоличествоСформированныхДокументов + ", из них с ошибками проведения - " + КоличествоСформированныхДокументовСОшибками + "."); МассивСообщенийПользователю.Добавить(""); КонецЕсли;
Проблема этого кода в том, что если он спотыкается на одном документе, то никогда не закончится.
Изначально в этом цикле было предусмотрено, что документы могут не провестись и даже ведется подсчет ошибочных документов. Проблема в том, что в случае ошибок будет создаваться один и тот же документ и произойдет зацикливание.
Учитывая, что документы формируются по таблице, я просто добавил в таблицу признак «Обработано» и «Номер», чтобы не адресоваться в таблице по трем измерениям.
После этого цикл имеет гарантированный конечный объем.
Объем: 1 час.
Свежие комментарии