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

Конечно, многопоточность – это не панацея, но это хороший способ для:

1) уменьшения длительности выполнения длительных процедур (в некоторых ситуациях - на порядки!);

2) повышения утилизации ресурсов оборудования.

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

Пара примеров:

1) Существует несколько сотен магазинов, работающих на самописном ПО, и единая база в головной компании, куда ежедневно выгружаются транзакции по продажам. Загрузка инициируется пользователем по кнопке и занимает длительное время, т.к. магазинов много и транзакций приличное количество, а обработка пакетов данных идет последовательно.

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

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

2) Существует большая база данных с транзакциями по продажам магазинов (из предыдущего примере) и есть необходимость периодически выгружать эти данные в стороннюю BI-систему средствами 1С. Выгрузка инициируется пользователем и занимает длительное время, т.к. системе нужно получить из базы данных миллионы строк и выгрузить их в промежуточную базу для BI. Во время выгрузки данных оборудование загружено несущественно.

В этой ситуации можно было бы распараллелить выгрузку данных – одновременно выгружать данные по разным магазинам. Это существенно ускорит процесс и позволит в полной мере ощутить эффект от мощного железа.

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

Наиболее эффективно организовать многопоточность можно с помощью Фоновых заданий (не путать с регламентными заданиями).
Необходимо циклически формировать порции фоновых заданий и дожидаться их завершения.

Пример кода для второго случая (выгрузка большого массива данных порциями):

1) Процедура, инициирующая многопоточное выполнение кода:

 Процедура КнопкаВыполнитьНажатие(Кнопка)

     //указывает число потоков, которые будут запущены одновременно
     ЧислоПараллельныхПотоков = 10;

     МассивЗаданий = Новый Массив;

     Запрос = Новый Запрос(
     "ВЫБРАТЬ РАЗЛИЧНЫЕ
     |    ПартииТоваровНаСкладах.Склад
     |ИЗ
     |    РегистрНакопления.ПартииТоваровНаСкладах КАК ПартииТоваровНаСкладах
     |ГДЕ
     |    ПартииТоваровНаСкладах.Период МЕЖДУ &Дата1 И &Дата2");
     Запрос.УстановитьПараметр("Дата1", ДатаНачала);
     Запрос.УстановитьПараметр("Дата2", ДатаОкончания);

     Результат = Запрос.Выполнить().Выгрузить();

     Для каждого Стр из Результат Цикл

         МассивПараметров = Новый Массив;
         МассивПараметров.Добавить(ДатаНачала);
         МассивПараметров.Добавить(ДатаОкончания);
         МассивПараметров.Добавить(Стр.Склад);

         Задание = ФоновыеЗадания.Выполнить("ВыгрузкаДанныхНаСервере.ВыгрузитьДанныеПоПартиям", МассивПараметров);

         МассивЗаданий.Добавить(Задание);

         Если МассивЗаданий.Количество() >= ЧислоПараллельныхПотоков Тогда
             Попытка
                 ФоновыеЗадания.ОжидатьЗавершения(МассивЗаданий);
             Исключение
             КонецПопытки;
             МассивЗаданий.Очистить();
         КонецЕсли;

     КонецЦикла;

     Если МассивЗаданий.Количество() > 0 Тогда
         Попытка
             ФоновыеЗадания.ОжидатьЗавершения(МассивЗаданий);
         Исключение
         КонецПопытки;
         МассивЗаданий.Очистить();
     КонецЕсли;

     Сообщить("Время выполнения процедуры - " + (ТекущаяДата() - ВремяНачала) + " с.");

КонецПроцедуры


2) Процедура, которую непосредственно выполняет фоновое задание (основная логика):

Общий модуль «ВыгрузкаДанныхНаСервере», выполняемый на сервере:

Процедура ВыгрузитьДанныеПоПартиям(ДатаНачала, ДатаОкончания, Склад) Экспорт

     Запрос = Новый Запрос(
     "ВЫБРАТЬ
     |    *
     |ИЗ
     |    РегистрНакопления.ПартииТоваровНаСкладах КАК ПартииТоваровНаСкладах
     |ГДЕ
     |    ПартииТоваровНаСкладах.Период МЕЖДУ &Дата1 И &Дата2
     |    И ПартииТоваровНаСкладах.Склад = &Склад");

     Запрос.УстановитьПараметр("Дата1", ДатаНачала);
     Запрос.УстановитьПараметр("Дата2", ДатаОкончания);
     Запрос.УстановитьПараметр("Склад", Склад);

     Результат = Запрос.Выполнить().Выгрузить();

     Для каждого Стр из Результат Цикл
         //Что-то делаем с данными
     КонецЦикла;

КонецПроцедуры


При этом следует учесть, что к выбору числа параллельных заданий нужно отнестись ответственно – если запустить их даже несколько десятков, то велика вероятность «повесить» и сервер приложений, и сервер СУБД.

необходимо зарегистрироваться для просмотра ссылки