Как равномерно разделить временной период на порции в 1С для многопоточной обработки

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

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

Проблема простого деления

На первый взгляд задача кажется тривиальной: взять общее количество дней и разделить на количество потоков. Однако на практике мы сталкиваемся с тем, что количество дней редко делится нацело. Рассмотрим пример: нам нужно разделить 28 дней на 10 порций. Если мы будем использовать простое округление Окр(28/10, 0) = 3, то получим 9 периодов по 3 дня (всего 27 дней) и один последний период в 1 день. Это создает неравномерную нагрузку. Если же мы будем использовать целую часть Цел(28/10) = 2, то получим 9 периодов по 2 дня (18 дней) и огромный «остаток» в 10 дней для последнего потока. Проанализируем, как сделать распределение более гармоничным.

Вариант 1: Алгоритм распределения остатка (Математический подход)

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

  1. Определяем общее количество единиц (дней или секунд) в периоде: ВсегоДней = (Конец - Начало) / 86400 + 1.
  2. Вычисляем базовый размер порции: Размер = Цел(ВсегоДней / КоличествоПотоков).
  3. Находим остаток: Остаток = ВсегоДней % КоличествоПотоков.
  4. Первые Остаток потоков получают Размер + 1 дней, остальные — просто Размер.

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

Вариант 2: Итеративный расчет среднего (Практический подход)

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

Предположим, нам нужно распределить 55 дней на 10 потоков:

Такой подход динамически подстраивает границы, минимизируя «ошибку» накопления. Проанализируем реализацию этого алгоритма на языке 1С.

Реализация алгоритма в коде 1С

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


// Предположим, у нас есть Период (СтандартныйПериод) и КоличествоПотоков = 10
КоличествоПотоков = 10;    
    
// Вычисляем общее количество дней в интервале
// Прибавляем 1, чтобы включить дату окончания (например, с 01.02 по 01.02 — это 1 день)
ОбщееКоличество = (НачалоДня(Период.ДатаОкончания) - НачалоДня(Период.ДатаНачала)) / 86400 + 1;

ОстатокДней = ОбщееКоличество;
ОстатокПорций = КоличествоПотоков; 
ГраницаПредыдущейПорции = НачалоДня(Период.ДатаНачала);

Для Сч = 1 по КоличествоПотоков Цикл
    
    // Рассчитываем размер текущей порции динамически
    РазмерПорции = Окр(ОстатокДней / ОстатокПорций, 0);    
    
    НижняяГраница = ГраницаПредыдущейПорции;
    // Граница рассчитывается как количество дней, умноженное на секунды в сутках
    ВерхняяГраница = НижняяГраница + (РазмерПорции * 86400) - 1; 

    // Здесь можно инициализировать фоновое задание или записать границы в массив
    Сообщить("Поток №" + Сч + ": с " + НижняяГраница + " по " + КонецДня(ВерхняяГраница));
    
    // Обновляем переменные для следующей итерации
    ОстатокДней = ОстатокДней - РазмерПорции; 
    ОстатокПорций = ОстатокПорций - 1;
    ГраницаПредыдущейПорции = КонецДня(ВерхняяГраница) + 1;
КонецЦикла;

Важные технические нюансы

Разберем несколько критически важных моментов, которые необходимо учитывать при работе с датами в 1С для многопоточности:

1. Точность до секунды. В 1С тип Дата содержит время. Если ваш алгоритм просто складывает дни, вы можете потерять данные, находящиеся внутри суток (например, документы, созданные в 23:59:59). Всегда используйте функции НачалоДня и КонецДня для четкого определения границ.

2. Границы периодов. Обратите внимание, что верхняя граница предыдущего периода не должна пересекаться с нижней границей следующего. Если поток 1 обрабатывает данные по 28.02.2019 23:59:59, то поток 2 должен начинаться строго с 01.03.2019 00:00:00. Использование конструкции КонецДня(Дата) + 1 — стандартный способ перейти к началу следующего дня.

3. Пустые периоды. Выясним, что произойдет, если количество дней в периоде меньше, чем количество затребованных потоков (например, 5 дней на 10 потоков). В предложенном выше итеративном алгоритме РазмерПорции в какой-то момент станет равным 0. Проанализируем ситуацию: в этом случае некоторые потоки получат пустые интервалы. Рекомендуется добавить проверку Если РазмерПорции > 0 Тогда, чтобы не запускать лишние фоновые задания без данных.

4. Распределение по записям vs Распределение по времени. Разделение по дням эффективно только в том случае, если плотность данных во времени примерно одинакова. Если же за один день месяца проведено 100 000 документов, а за все остальные — по 10, то деление по периодам не даст выигрыша в скорости. В таких случаях лучше использовать деление по квантам (например, по 1000 записей) или по остатку от деления внутреннего идентификатора Ссылка (GUID) документа.

Заключение

Равномерное распределение задач — залог стабильной работы сервера. Использование итеративного метода с округлением среднего остатка (Окр(ОстатокДней / ОстатокПорций, 0)) является наиболее простым и надежным способом разделения периодов в 1С. Это избавляет разработчика от необходимости вручную обрабатывать «хвосты» и гарантирует, что каждый поток получит максимально близкий объем работы.

← На главную