Почему при автообновлении одного списка документов в 1С обновляются все списки на форме и как это исправить?

Программист 1С v8.3 (Обычные формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

При разработке сложных интерфейсов в обычном приложении 1С (толстый клиент), таких как «Рабочее место менеджера» или «Монитор заказов», разработчики часто сталкиваются с неприятным эффектом. На форме размещено несколько табличных полей (списков документов или справочников), и для одного из них включено стандартное автообновление. Однако при срабатывании таймера платформа инициирует обновление всех списков на форме, что приводит к лавинообразному вызову тяжелых процедур ПриПолученииДанных и ПриВыводеСтроки. В результате интерфейс «замирает», а нагрузка на сервер и клиентскую часть резко возрастает.

В этой статье мы подробно разберем, почему платформа ведет себя именно так, проанализируем технические причины и выработаем комплекс мер по оптимизации производительности таких форм.

Причины избыточного обновления: особенности архитектуры обычного приложения

Прежде всего, выясним причину такого поведения. В обычном приложении графический интерфейс тесно связан с операционной системой через Win32 API. Когда один из элементов управления (например, ДокументСписок) сообщает системе, что его данные изменились и их нужно перерисовать, платформа часто инициирует цикл перерисовки всей клиентской области формы.

Рассмотрим ключевые факторы, влияющие на это:

  1. Единый цикл отрисовки (Invalidate Rect): Принудительное обновление одного табличного поля может посылать сигнал WM_PAINT всей форме. Платформа 1С в ответ на это проверяет актуальность данных во всех видимых элементах.
  2. Общий кэш объектов: Динамические списки в обычном приложении работают в рамках одного программного контекста. Если механизм оповещения об изменениях в базе данных фиксирует транзакцию, он может пометить все связанные с типом данных списки как «грязные» (требующие обновления).
  3. Синхронизация данных: Платформа стремится поддерживать целостность отображения. Если на форме представлены разные списки, которые потенциально могут быть связаны (например, заказы и реализации), 1С перестраховывается и обновляет все элементы, чтобы пользователь не видел противоречивых данных.

Таким образом, срабатывание события ПриПолученииДанных на «соседнем» списке при обновлении основного — это не баг конкретной версии, а заложенная логика работы толстого клиента.

Анализ влияния событий ПриПолученииДанных и ПриВыводеСтроки

Разберем подробнее, почему именно эти события становятся узким местом. Процедура ПриВыводеСтроки вызывается для каждой ячейки каждой строки, попадающей в видимую область. Если у вас 20 списков и форма перерисовывается раз в 5–10 секунд, количество вызовов исчисляется тысячами.

Процедура ПриПолученииДанных более эффективна, так как она работает с массивом строк (оформлений), но и она может стать критическим фактором, если внутри выполняются:

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

Решение 1: Программное управление обновлением через обработчик ожидания

Самый эффективный способ борьбы с этой проблемой — полный отказ от стандартного свойства «Автообновление» в настройках табличного поля. Вместо него мы будем использовать программный метод ПодключитьОбработчикОжидания(). Это позволит нам полностью контролировать процесс.

Рассмотрим пример реализации. В модуле формы определим процедуру обновления:


Процедура ПриОткрытии()
    // Вместо стандартного автообновления раз в 60 секунд
    ПодключитьОбработчикОжидания("ОбновитьДанныеСписков", 60);
КонецПроцедуры

Процедура ОбновитьДанныеСписков() Экспорт
    // Мы можем обновлять списки выборочно
    ЭлементыФормы.СписокЗаказов.Обновить();
    
    // Или вводить условия, например, обновлять только если страница активна
    Если ЭлементыФормы.ОсновнаяПанель.ТекущаяСтраница = ЭлементыФормы.ОсновнаяПанель.Страницы.Склад Тогда
        ЭлементыФормы.СписокОстатков.Обновить();
    КонецЕсли;
КонецПроцедуры

Этот подход позволяет избежать одновременного удара по производительности со стороны всех списков сразу. Мы можем разнести их обновление во времени, используя разные обработчики с разными интервалами.

Решение 2: Оптимизация кода в ПриПолученииДанных

Если мы не можем избежать вызова события, мы должны сделать его максимально «легким». Основное правило: никаких запросов в цикле. Посмотрим на правильный паттерн использования ПриПолученииДанных:


Процедура СписокПриПолученииДанных(Элемент, ОформленияСтрок)
    // 1. Собираем все нужные ссылки в массив
    МассивСсылок = Новый Массив;
    Для Каждого Оформление Из ОформленияСтрок Цикл
        МассивСсылок.Добавить(Оформление.ДанныеСтроки.Ссылка);
    КонецЦикла;

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

    // 3. Распределяем данные по строкам
    Для Каждого Оформление Из ОформленияСтрок Цикл
        СтрокаДанных = ТаблицаРезультатов.Найти(Оформление.ДанныеСтроки.Ссылка, "Ссылка");
        Если СтрокаДанных <> Неопределено Тогда
            Оформление.Ячейки.Статус.УстановитьТекст(СтрокаДанных.Состояние);
        КонецЕсли;
    КонецЦикла;
КонецПроцедуры

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

Решение 3: Использование кэширования на стороне формы

Выясним еще один важный момент: часто данные, которые выводятся в списках, не меняются так часто, как происходит перерисовка. Мы можем внедрить механизм кэширования. Создадим переменную модуля формы мКэшДанных (типа Соответствие) и будем хранить в ней результаты расчетов.

Рассмотрим логику:

  1. В начале процедуры ПриПолученииДанных проверяем, есть ли данные в кэше и не устарели ли они.
  2. Если данные свежие — берем их из памяти.
  3. Если данных нет или прошло более 5 минут — выполняем запрос и обновляем кэш.

Это кардинально снижает нагрузку, даже если платформа вызывает обновление списка каждые 5 секунд из-за соседнего элемента.

Дополнительные рекомендации по работе со списками

Проанализируем еще несколько приемов, которые помогут ускорить работу тяжелых форм:

Резюме

Поведение 1С в обычном приложении, при котором обновление одного списка инициирует перерисовку всей формы, является фундаментальной особенностью платформы. Чтобы интерфейс оставался отзывчивым, рекомендуем придерживаться следующей стратегии: отказ от стандартного автообновления в пользу ПодключитьОбработчикОжидания, групповая обработка данных в событии ПриПолученииДанных и минимизация количества списков на одной форме. В долгосрочной перспективе лучшим решением станет переход на Управляемые формы, где механизм ДинамическийСписок работает гораздо оптимальнее и изолированнее.

← На главную