Правильная индикация прогресса цикла
Работу некоторых циклов с большим количеством проходов разумно сопровождать индикацией прогресса. Но зачастую сама эта индикация и становится главным тормозом процесса. Так как частенько на практике встречаю подобные огрехи, решил опубликовать пример правильной индикации.
Так как индикатор представляет собой элемент графического интерфейса пользователя, то любая операция с ним выполняется относительно долго, будь то строка состояния, индикатор или даже надпись. Поэтому в случае большого количества проходов цикла обновление индикатора на каждом проходе может стать главным потребителем вычислительных ресурсов компьютера. Хотя такие ситуации легко обнаруживаются с помощью замера производительности, все же лучше сразу предусматривать подобные проблемы.
Привожу 2 вспомогательных метода, которые позволят просто и эффективно реализовать индикацию любого цикла с известным максимальным значением счетчика.
// Получает структуру для индикации прогресса цикла.
// // Параметры: // КоличествоПроходов – Число - максимальное значение счетчика; // ПредставлениеПроцесса – Строка, "Выполнено" – отображаемое название процесса; // ВнутреннийСчетчик - Булево, *Истина - использовать внутренний счетчик с начальным значением 1, // иначе нужно будет передавать значение счетчика при каждом вызове обновления индикатора; // КоличествоОбновлений - Число, *100 - всего количество обновлений индикатора; // ЛиВыводитьВремя - Булево, *Истина - выводить приблизительное время до окончания процесса; // РазрешитьПрерывание - Булево, *Истина - разрешает пользователю прерывать процесс. // // Возвращаемое значение: // Структура - которую потом нужно будет передавать в метод ЛксОбработатьИндикатор. // Функция ЛксПолучитьИндикаторПроцесса(КоличествоПроходов, ПредставлениеПроцесса = "Выполнено", ВнутреннийСчетчик = Истина, КоличествоОбновлений = 100, ЛиВыводитьВремя = Истина, РазрешитьПрерывание = Истина) Экспорт
Индикатор = Новый Структура; Индикатор.Вставить("КоличествоПроходов", КоличествоПроходов); Индикатор.Вставить("ДатаНачалаПроцесса", ТекущаяДата()); Индикатор.Вставить("ПредставлениеПроцесса", ПредставлениеПроцесса); Индикатор.Вставить("ЛиВыводитьВремя", ЛиВыводитьВремя); Индикатор.Вставить("РазрешитьПрерывание", РазрешитьПрерывание); Индикатор.Вставить("ВнутреннийСчетчик", ВнутреннийСчетчик); Индикатор.Вставить("Шаг", КоличествоПроходов / КоличествоОбновлений); Индикатор.Вставить("СледующийСчетчик", 0); Индикатор.Вставить("Счетчик", 0); Возврат Индикатор;
КонецФункции // ЛксПолучитьИндикаторПроцесса()
// Проверяет и обновляет индикатор. Нужно вызывать на каждом проходе индицируемого цикла. // // Параметры: // Индикатор – Структура – индикатора, полученная методом ЛксПолучитьИндикаторПроцесса; // Счетчик – Число – внешний счетчик цикла, используется при ВнутреннийСчетчик = Ложь. // Процедура ЛксОбработатьИндикатор(Индикатор, Счетчик = 0) Экспорт
Если Индикатор.ВнутреннийСчетчик Тогда Индикатор.Счетчик = Индикатор.Счетчик + 1; Счетчик = Индикатор.Счетчик; КонецЕсли; Если Индикатор.РазрешитьПрерывание Тогда ОбработкаПрерыванияПользователя(); КонецЕсли; Если Счетчик > Индикатор.СледующийСчетчик Тогда Индикатор.СледующийСчетчик = Цел(Счетчик + Индикатор.Шаг); Если Индикатор.ЛиВыводитьВремя Тогда ПрошлоВремени = ТекущаяДата() - Индикатор.ДатаНачалаПроцесса; Осталось = ПрошлоВремени * (Индикатор.КоличествоПроходов / Счетчик - 1); Часов = Цел(Осталось / 3600); Осталось = Осталось - (Часов * 3600); Минут = Цел(Осталось / 60); Секунд = Цел(Цел(Осталось - (Минут * 60))); ОсталосьВремени = Формат(Часов, "ЧЦ=2; ЧН=00; ЧВН=") + ":" + Формат(Минут, "ЧЦ=2; ЧН=00; ЧВН=") + ":" + Формат(Секунд, "ЧЦ=2; ЧН=00; ЧВН="); ТекстОсталось = "Осталось: ~" + ОсталосьВремени; Иначе ТекстОсталось = ""; КонецЕсли; ТекстСостояния = Индикатор.ПредставлениеПроцесса + " " + Формат(Счетчик / Индикатор.КоличествоПроходов * 100, "ЧЦ=3; ЧДЦ=0") + "% " + ТекстОсталось;
Состояние(ТекстСостояния); КонецЕсли; Если Счетчик = Индикатор.КоличествоПроходов Тогда Состояние(""); КонецЕсли;
КонецПроцедуры // ЛксОбработатьИндикатор()
Ключевым моментом в ЛксОбработатьИндикатор() является количество обновлений индикатора (оно ограничено сверху КоличествоОбновлений=100).
Вот пример их использования.
КоличествоДанных = 100000; Индикатор = ЛксПолучитьИндикаторПроцесса(КоличествоДанных, "Проверка данных"); Для Счетчик = 1 По КоличествоДанных Цикл ЛксОбработатьИндикатор(Индикатор, Счетчик); КонецЦикла;
КоличествоДанных = 100000; Индикатор = ЛксПолучитьИндикаторПроцесса(КоличествоДанных, "Проверка данных", Истина); Для Счетчик = 1 По КоличествоДанных Цикл ЛксОбработатьИндикатор(Индикатор); КонецЦикла;
Вот как это выглядит визуально
|