Для начала на всякий случай вспомним, что регламентные и фоновые задания - это не совсем про одно и то же. Механизм регламентных заданий - это по сути планировщик, который будет запускать определенное фоновое задание с заданной периодичностью. Фоновое задание - это отдельный процесс (он же поток, он же нить, он же Жора, он же Гоша... не будем вдаваться в детали), исполняющий часть работы приложения. Фоновые задания мы можем создавать/запускать по мере необходимости, чем и воспользуемся для решения задачи ускорения вычислений.
В рассматриваемом примере имеем http-сервис, выполняющий вычисления, которые могут быть абсолютно любыми: от "2*2" до запросов к БД. Наш сервис будет вычислять квадрат для каждого из переданного в массиве числа. Если размерность массива превысит определенный порог, то будет применяться распараллеливание. В реальных задачах к этому лучше отнестись внимательно, т.к. из-за излишних накладных расходов на организацию параллелизма можно получить деградацию времени отклика сервиса. В качестве инструмента организации параллелизма будем использовать фоновые задания.
Зачастую ответом сервиса являются данные, отформатированные в определенной нотации. Никто не ограничивает нас в раздельном формировании частей общего ответа. Ведь мы можем сформировать несколько частей JSON, а затем объединить их в один большой результат? Конечно можем. Так и будем делать.
Ниже представлена основная логика с комментариями
//
// точка входа обработки тела запроса
// тело запроса json-массив числовых значений [1, 2, 3, 4, 5, 6, 7]
// результатом исполнения будет массив квадратов исходных чисел [1, 4, 9, 16, 25, 36, 49]
Функция Квадрат(ТелоЗапроса) Экспорт
Результат = Новый Массив;
Попытка
// получаем коллекцию из входных данных
ВходящийМассив = ИзВеба(ТелоЗапроса);
// служебная структура, которая содержит все необходимые для основного процесса вычислений параметры
ПараметрыВычисления = СтруктураПараметровВычисления();
ПараметрыВычисления.ВходящийМассив = ВходящийМассив;
// определяем необходимость распараллеливания работы
КоличествоНаПроцесс = 2;
Если ВходящийМассив.Количество() > КоличествоНаПроцесс Тогда
// реализуем параллельную обработку
// разделяем данные на части
МассивЧастей = РазделитьНаЧастиЗаданнойРазмерности(ВходящийМассив, КоличествоНаПроцесс);
// формируем параметры исполнения для каждого процесса
НомерПакета = 0;
МассивПараметровФоновыхЗаданий = Новый Массив;
Для Каждого Часть из МассивЧастей Цикл
ТекущиеПараметрыВычисления = СтруктураПараметровВычисления();
ТекущиеПараметрыВычисления.ВходящийМассив = Часть;
ТекущиеПараметрыВычисления.НомерПакета = НомерПакета;
ПараметрыФоновогоЗадания = Новый Массив;
ПараметрыФоновогоЗадания.Добавить(ТекущиеПараметрыВычисления);
МассивПараметровФоновыхЗаданий.Добавить(ПараметрыФоновогоЗадания);
НомерПакета = НомерПакета + 1;
КонецЦикла;
// выполняем вычисления
// для этого запускаем фоновые задания, ожидаем их завершения и собираем переданные сообщения
// каждое сообщение - это кусочек вычисления, которые необходимо будет просто собрать вместе в один общий ответ
МетодОбработки = "Вычислитель.ВычислитьКвадрат";
СообщенияФоновыхПроцессов = ВыполнитьПроцессы(МетодОбработки, МассивПараметровФоновыхЗаданий);
// получение и сортировка результата
// сортировка может потребоваться, если результат сервиса должен быть собран в строго определенном порядке
ТаблицаСообщений = Новый ТаблицаЗначений;
ТаблицаСообщений.Колонки.Добавить("НомерПакета");
ТаблицаСообщений.Колонки.Добавить("Результат");
Для Каждого ТекущееСообщение Из СообщенияФоновыхПроцессов Цикл
ЗаполнитьЗначенияСвойств(ТаблицаСообщений.Добавить(), ИзВеба(ТекущееСообщение.Значение));
КонецЦикла;
ТаблицаСообщений.Сортировать("НомерПакета");
// компоновка ответа сервиса
Для Каждого ТекущееСообщение Из ТаблицаСообщений Цикл
ТекущийРезультат = ТекущееСообщение.Результат;
Для Каждого ТекущийЭлемент Из ТекущийРезультат Цикл
Результат.Добавить(ТекущийЭлемент);
КонецЦикла;
КонецЦикла;
Иначе
// сюда попадаем, если не нужно распараллеливать
Результат = Вычислитель.ВычислитьКвадрат(ПараметрыВычисления);
КонецЕсли;
Исключение
ОписаниеОшибки = ОписаниеОшибки();
ЗаписьЖурналаРегистрации(ИмяСобытияЖурнала(), УровеньЖурналаРегистрации.Ошибка,,, ОписаниеОшибки);
КонецПопытки;
Возврат ВВеб(Результат);
КонецФункции
А вот здесь можно увидеть, как фоновые задания передают результат исполнения
//
//
Функция ВычислитьКвадрат(ПараметрыВычисления) Экспорт
Результат = Новый Массив;
// выполняем вычисление квадрата для каждого числа
Для Каждого Элемент Из ПараметрыВычисления.ВходящийМассив Цикл
ТекущееЧисло = КакЧисло(Элемент);
Результат.Добавить(ТекущееЧисло*ТекущееЧисло);
КонецЦикла;
// определяем необходимость передачи результата
// если контекст текущего исполнения - это фоновое задание, то выполняется сообщение результата для родительского процесса
Если ИсполнениеВФоновомПроцессе() Тогда
Сообщение = Новый СообщениеПользователю;
Сообщение.ИдентификаторНазначения = Новый УникальныйИдентификатор;
Сообщение.Текст = ЗначениеВСтрокуВнутр(Новый ХранилищеЗначения(ВВеб(Новый Структура("НомерПакета, Результат", ПараметрыВычисления.НомерПакета, Результат)), Новый СжатиеДанных(9)));
Сообщение.Сообщить();
КонецЕсли;
Возврат Результат;
КонецФункции
Как мы видим, реализация очень простая и может быть применена для большого количества задач. Отдельно отмечу, что речь идет не только о задачах обработки передаваемых на вход данных, но и о задачах, для реализации которых необходимо обращение к БД. Например, для формирования ответа сервиса нам необходимо осуществить выборку данных из БД за переданный в параметрах сервиса период времени. Можно разбить переданный период на несколько частей, каждую часть обработать с использованием параллельных процессов, а затем собрать результаты в общий ответ.
Чего мы можем добиться, используя описанный подход? В первую очередь быстродействие: параллельная обработка одного и того же набора данных может быть быстрее, чем последовательная. Во-вторых, надежность: уменьшение пакетов обработки может благотворно сказаться на показателях надежности. В-третьих, масштабируемость: можно распределять фоновые задания в кластере между нодами через ТНФ.
Полная реализация описанного примера приведена во вложении к публикации. Конфигурация с примером (платформа 8.3.17).