В данной статье рассмотрим примеры выполнения функций и процедур в многопоточном режиме, используя возможности Библиотеки стандартных подсистем.
Введение
В этой статье решил привести небольшой программный эксперимент - позапускать собственную процедуру общего модуля конфигурации в многопоточном режиме, используя относительно новые методы библиотеки стандартных подсистем - набора модулей ДлительныеОперации. Разработку примеров я буду вести на типовой конфигурации БСП - версии 3.1.7 на Платформе 1с 8.3.20.
Статью разобью на две части - первая часть описывает процедуру, которая должна будет запускаться многопоточно, вторая - практическое использование запуска в несколько потоков - ВыполнитьПроцедуруВНесколькоПотоков.
Напишем процедуру, которая будет использоваться в многопоточном запуске
В собственном общем модуле конфигурации (я работаю на типовой БСП 3.1.7), создаем искусственную процедуру, которую будем использовать в многопоточном запуске. Выглядит она пусть так:
&НаСервере
Процедура ПроцедураМногопоток(Поток, Параметры) Экспорт
КоличествоСозданныхОбъектов = 0;
Если Параметры.ТипОбработки = "Вариант 1" Тогда // Создадим 500 документов поступления (можно и загрузить)
Для Счетчик = 1 по 500 Цикл
НовДок = Документы._ДемоПоступлениеТоваров.СоздатьДокумент();
НовДок.Дата = ТекущаяДата();
НовДок.Организация = Параметры.Организация;
НовДок.Валюта = Справочники.Валюты.НайтиПоКоду("643");
НовДок.Комментарий = Параметры.КомментарийДокумента;
Попытка
НовДок.Записать(РежимЗаписиДокумента.Запись);
Исключение
КонецПопытки;
КоличествоСозданныхОбъектов = КоличествоСозданныхОбъектов + 1;
КонецЦикла;
ИначеЕсли Параметры.ТипОбработки = "Вариант 2" Тогда // Создадим 1000 записей справочника "Номенклатура"
Для Счетчик = 1 по 1000 Цикл
НовСпр = Справочники._ДемоНоменклатура.СоздатьЭлемент();
НовСпр.Наименование = "Новая номенклатура_"+Строка(Счетчик);
НовСпр.НаименованиеДляПечати = "Новая номенклатура_"+Строка(Параметры.ОписаниеСправочника);
НовСпр.Записать();
КонецЦикла;
КоличествоСозданныхОбъектов = КоличествоСозданныхОбъектов + 1;
КонецЕсли;
КонецПроцедуры
При первом типе обработке - "Вариант 1", процедура - будет создать 500 документов. При втором типе - "Вариант 2" - запустим цикл по созданию 1000 элементов справочника "Номенклатура".
Теперь, перейдем к коду обработки, использующим ДлительныеОперации, запускающим ж
Описание функционала запуска с помощью функции "ВыполнитьПроцедуруВНесколькоПотоков"
Для начала нарисуем вот такую форму:
![Рис.1 Форма для исследования многопоточности Рис.1 Форма для исследования многопоточности](https://habrastorage.org/getpro/habr/upload_files/168/c2b/918/168c2b9188e8f3b7fd4bdf64303c43ce.jpg)
Напишем процедуру клиентского запуска. Выглядит она вот так:
&НаКлиенте
Процедура ЗапуститьМногопоток(Команда)
ОповещениеОПрогрессеВыполнения = Новый ОписаниеОповещения("ПрогрессВыполнения", ЭтотОбъект);
Задание = СоздадимПотокиНаСервере(УникальныйИдентификатор);
ИдентификаторЗадания = Задание.ИдентификаторЗадания;
НастройкиОжидания = ДлительныеОперацииКлиент.ПараметрыОжидания(ЭтотОбъект);
НастройкиОжидания.ВыводитьОкноОжидания = Ложь;
НастройкиОжидания.ОповещениеОПрогрессеВыполнения = ОповещениеОПрогрессеВыполнения;
НастройкиОжидания.ВыводитьПрогрессВыполнения = Истина;
Обработчик = Новый ОписаниеОповещения("ОбработатьДанные", ЭтотОбъект);
ДлительныеОперацииКлиент.ОжидатьЗавершение(Задание, Обработчик, НастройкиОжидания);
КонецПроцедуры
Далее, функция СоздадимПотокиНаСервере(УИД)
&НаСервере
Функция СоздадимПотокиНаСервере(УникальныйИдентификатор)
ПараметрыВыполнения = ДлительныеОперации.ПараметрыВыполненияВФоне(УникальныйИдентификатор);
ПараметрыВыполнения.НаименованиеФоновогоЗадания = НСтр("ru = 'Выполнение многопоточной функции'");
ПараметрыВыполнения.ЗапуститьВФоне = Истина;
ПараметрыМетода = Новый Соответствие();
// ===== первый поток выполнения =====
КодПотока = "Поток 1";
ПараметрыПотока = ПараметрыМногопоточнойФункции();
ПараметрыПотока.ТипОбработки = "Вариант 1";
ПараметрыПотока.ОписаниеСправочника = Объект.ОписаниеСправочника;
ПараметрыПотока.КомментарийДокумента = Объект.КомментарийДокумента;
ПараметрыПотока.Организация = Объект.Организация;
ПараметрыВызоваСервера = Новый Массив;
ПараметрыВызоваСервера.Добавить(КодПотока); // КодПотока
ПараметрыВызоваСервера.Добавить(ПараметрыПотока); // Параметры функции в потоке
ПараметрыМетода.Вставить(КодПотока,ПараметрыВызоваСервера);
// ===== второй поток выполнения =====
КодПотока = "Поток 2";
ПараметрыПотока = ПараметрыМногопоточнойФункции();
ПараметрыПотока.ТипОбработки = "Вариант 2";
ПараметрыПотока.ОписаниеСправочника = Объект.ОписаниеСправочника;
ПараметрыПотока.КомментарийДокумента = Объект.КомментарийДокумента;
ПараметрыПотока.Организация = Объект.Организация;
ПараметрыВызоваСервера = Новый Массив;
ПараметрыВызоваСервера.Добавить(КодПотока); // КодПотока
ПараметрыВызоваСервера.Добавить(ПараметрыПотока); // Параметры функции в потоке
ПараметрыМетода.Вставить(КодПотока,ПараметрыВызоваСервера);
// ===================================
ИмяФункцииИлиПроцедуры = "ОбщийМодуль1.ПроцедураМногопоток";
ФоновоеЗадание = ДлительныеОперации.ВыполнитьПроцедуруВНесколькоПотоков(ИмяФункцииИлиПроцедуры, ПараметрыВыполнения, ПараметрыМетода);
Если ФоновоеЗадание.Статус = "Ошибка" Тогда
Сообщить(СокрЛП(ФоновоеЗадание.Статус));
КонецЕсли;
Возврат ФоновоеЗадание;
КонецФункции
&НаСервере
Функция ПараметрыМногопоточнойФункции()
Результат = Новый Структура();
Результат.Вставить("ТипОбработки", 0);
Результат.Вставить("ОписаниеСправочника", "");
Результат.Вставить("КомментарийДокумента", "");
Результат.Вставить("Организация", Справочники._ДемоОрганизации.ПустаяСсылка());
Результат.Вставить("ЗагружатьПорциями", Истина);
Возврат Результат;
КонецФункции
Здесь, я обозначаю условные параметры для двух потоков "Поток 1" и "Поток 2". Далее, использую Функцию "ВыполнитьПроцедуруВНесколькоПотоков" с этими параметрами.
Опишем функции "ПрогрессВыполнения" и "ОбработатьДанные" (это будут чисто-условные "полупустые" функции)
&НаКлиенте
Процедура ПрогрессВыполнения(Результат, ДополнительныеПараметры) Экспорт
КонецПроцедуры
&НаСервереБезКонтекста
Функция ПрочитатьПрогресс(ИдентификаторЗадания)
Возврат ДлительныеОперации.ПрочитатьПрогресс(ИдентификаторЗадания);
КонецФункции
&НаКлиенте
Процедура ОбработатьДанные(Задание, ДополнительныеПараметры) Экспорт
Если Задание = Неопределено Тогда
Возврат;
КонецЕсли;
ЭтаФорма.РезультатВыполнения = СокрЛП(Задание.Статус) +" "+СокрЛП(ПолучитьИЗВременногоХранилища(Задание.АдресРезультата));
КонецПроцедуры
Весь код модуля формы обработки вот такой (можно скопипастить):
&НаСервере
Функция ПараметрыМногопоточнойФункции()
Результат = Новый Структура();
Результат.Вставить("ТипОбработки", 0);
Результат.Вставить("ОписаниеСправочника", "");
Результат.Вставить("КомментарийДокумента", "");
Результат.Вставить("Организация", Справочники._ДемоОрганизации.ПустаяСсылка());
Результат.Вставить("ЗагружатьПорциями", Истина);
Возврат Результат;
КонецФункции
&НаКлиенте
Процедура ЗапуститьМногопоток(Команда)
ОповещениеОПрогрессеВыполнения = Новый ОписаниеОповещения("ПрогрессВыполнения", ЭтотОбъект);
Задание = СоздадимПотокиНаСервере(УникальныйИдентификатор);
//ИдентификаторЗадания = ПолучитьИЗВременногоХранилища(Задание.АдресРезультата).Получить("Поток 1").ИдентификаторЗадания;//Задание.ИдентификаторЗадания;
ИдентификаторЗадания = Задание.ИдентификаторЗадания;
//Сообщить(ИдентификаторЗадания);
НастройкиОжидания = ДлительныеОперацииКлиент.ПараметрыОжидания(ЭтотОбъект);
НастройкиОжидания.ВыводитьОкноОжидания = Ложь;
НастройкиОжидания.ОповещениеОПрогрессеВыполнения = ОповещениеОПрогрессеВыполнения;
НастройкиОжидания.ВыводитьПрогрессВыполнения = Истина;
Обработчик = Новый ОписаниеОповещения("ОбработатьДанные", ЭтотОбъект);
ДлительныеОперацииКлиент.ОжидатьЗавершение(Задание, Обработчик, НастройкиОжидания);
КонецПроцедуры
&НаСервере
Функция СоздадимПотокиНаСервере(УникальныйИдентификатор)
ПараметрыВыполнения = ДлительныеОперации.ПараметрыВыполненияВФоне(УникальныйИдентификатор);
ПараметрыВыполнения.НаименованиеФоновогоЗадания = НСтр("ru = 'Выполнение многопоточной функции'");
ПараметрыВыполнения.ЗапуститьВФоне = Истина;
ПараметрыМетода = Новый Соответствие();
// ===== первый поток выполнения =====
КодПотока = "Поток 1";
ПараметрыПотока = ПараметрыМногопоточнойФункции();
ПараметрыПотока.ТипОбработки = "Вариант 1";
ПараметрыПотока.ОписаниеСправочника = Объект.ОписаниеСправочника;
ПараметрыПотока.КомментарийДокумента = Объект.КомментарийДокумента;
ПараметрыПотока.Организация = Объект.Организация;
ПараметрыВызоваСервера = Новый Массив;
ПараметрыВызоваСервера.Добавить(КодПотока); // КодПотока
ПараметрыВызоваСервера.Добавить(ПараметрыПотока); // Параметры функции в потоке
ПараметрыМетода.Вставить(КодПотока,ПараметрыВызоваСервера);
// ===== второй поток выполнения =====
КодПотока = "Поток 2";
ПараметрыПотока = ПараметрыМногопоточнойФункции();
ПараметрыПотока.ТипОбработки = "Вариант 2";
ПараметрыПотока.ОписаниеСправочника = Объект.ОписаниеСправочника;
ПараметрыПотока.КомментарийДокумента = Объект.КомментарийДокумента;
ПараметрыПотока.Организация = Объект.Организация;
ПараметрыВызоваСервера = Новый Массив;
ПараметрыВызоваСервера.Добавить(КодПотока); // КодПотока
ПараметрыВызоваСервера.Добавить(ПараметрыПотока); // Параметры функции в потоке
ПараметрыМетода.Вставить(КодПотока,ПараметрыВызоваСервера);
// ===================================
ИмяФункцииИлиПроцедуры = "ОбщийМодуль1.ПроцедураМногопоток";
ФоновоеЗадание = ДлительныеОперации.ВыполнитьПроцедуруВНесколькоПотоков(ИмяФункцииИлиПроцедуры, ПараметрыВыполнения, ПараметрыМетода);
Если ФоновоеЗадание.Статус = "Ошибка" Тогда
Сообщить(СокрЛП(ФоновоеЗадание.Статус));
КонецЕсли;
Возврат ФоновоеЗадание;
КонецФункции
&НаКлиенте
Процедура ПрогрессВыполнения(Результат, ДополнительныеПараметры) Экспорт
КонецПроцедуры
&НаСервереБезКонтекста
Функция ПрочитатьПрогресс(ИдентификаторЗадания)
Возврат ДлительныеОперации.ПрочитатьПрогресс(ИдентификаторЗадания);
КонецФункции
&НаКлиенте
Процедура ОбработатьДанные(Задание, ДополнительныеПараметры) Экспорт
Если Задание = Неопределено Тогда
Возврат;
КонецЕсли;
ЭтаФорма.РезультатВыполнения = СокрЛП(Задание.Статус) +" "+СокрЛП(ПолучитьИЗВременногоХранилища(Задание.АдресРезультата));
КонецПроцедуры
Заключение и выводы
В статье попробовал использовать функционал многопоточности - запустил искусственную процедуру общего модуля с разными параметрами параллельно (при одних значениях параметрах). Задача выполнена. Спасибо за прочтение материала, надеюсь, что данная статья будет кому-то полезна.
vis_inet
Спасибо.
Скажите, какие могут быть реальные ситуации для использования возможности многопоточного выполнения?
akabrr
Например обработка большого количества объектов за ограниченное время.
vis_inet
Это понятно, для разработчика может быть полезно.
А в типовых конфигурациях где это может быть применимо?