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

Введение

В этой статье решил привести небольшой программный эксперимент - позапускать собственную процедуру общего модуля конфигурации в многопоточном режиме, используя относительно новые методы библиотеки стандартных подсистем - набора модулей ДлительныеОперации. Разработку примеров я буду вести на типовой конфигурации БСП - версии 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 Форма для исследования многопоточности

Напишем процедуру клиентского запуска. Выглядит она вот так:

&НаКлиенте
Процедура ЗапуститьМногопоток(Команда)
	
	ОповещениеОПрогрессеВыполнения = Новый ОписаниеОповещения("ПрогрессВыполнения", ЭтотОбъект);
	
	
	Задание = СоздадимПотокиНаСервере(УникальныйИдентификатор);
	
	ИдентификаторЗадания = Задание.ИдентификаторЗадания;
	
	НастройкиОжидания = ДлительныеОперацииКлиент.ПараметрыОжидания(ЭтотОбъект);
	НастройкиОжидания.ВыводитьОкноОжидания = Ложь;
	НастройкиОжидания.ОповещениеОПрогрессеВыполнения = ОповещениеОПрогрессеВыполнения;
	НастройкиОжидания.ВыводитьПрогрессВыполнения = Истина;
	

	Обработчик = Новый ОписаниеОповещения("ОбработатьДанные", ЭтотОбъект);
	ДлительныеОперацииКлиент.ОжидатьЗавершение(Задание, Обработчик, НастройкиОжидания);
	
	
КонецПроцедуры

Далее, функция СоздадимПотокиНаСервере(УИД)

&НаСервере
Функция СоздадимПотокиНаСервере(УникальныйИдентификатор)

	ПараметрыВыполнения = ДлительныеОперации.ПараметрыВыполненияВФоне(УникальныйИдентификатор);
	ПараметрыВыполнения.НаименованиеФоновогоЗадания = НСтр("ru = 'Выполнение многопоточной функции'");
	ПараметрыВыполнения.ЗапуститьВФоне = Истина;
	
	ПараметрыМетода = Новый Соответствие();

	// ===== первый поток выполнения =====
	
	КодПотока = "Поток 1";
	ПараметрыПотока 						= ПараметрыМногопоточнойФункции();
	ПараметрыПотока.ТипОбработки 			= "Вариант 1";
	ПараметрыПотока.ОписаниеСправочника 	= Объект.ОписаниеСправочника; 
	ПараметрыПотока.КомментарийДокумента 	= Объект.КомментарийДокумента;
	ПараметрыПотока.Организация           	= Объект.Организация;	
	
	ПараметрыВызоваСервера = Новый Массив;
	ПараметрыВызоваСервера.Добавить(КодПотока); 		 // КодПотока
	ПараметрыВызоваСервера.Добавить(ПараметрыПотока);    // Параметры функции в потоке
	
	ПараметрыМетода.Вставить(КодПотока,ПараметрыВызоваСервера); 
	
	// ===== второй поток выполнения =====
	
	КодПотока = "Поток 2";
	ПараметрыПотока 						= ПараметрыМногопоточнойФункции();
	ПараметрыПотока.ТипОбработки 			= "Вариант 2";
	ПараметрыПотока.ОписаниеСправочника 	= Объект.ОписаниеСправочника; 
	ПараметрыПотока.КомментарийДокумента 	= Объект.КомментарийДокумента;
	ПараметрыПотока.Организация           	= Объект.Организация;	
	
	ПараметрыВызоваСервера = Новый Массив;
	ПараметрыВызоваСервера.Добавить(КодПотока); 		 // КодПотока
	ПараметрыВызоваСервера.Добавить(ПараметрыПотока);    // Параметры функции в потоке
	
	ПараметрыМетода.Вставить(КодПотока,ПараметрыВызоваСервера); 	
	
	// =================================== 
	
	ИмяФункцииИлиПроцедуры = "ОбщийМодуль1.ПроцедураМногопоток";
	
	ФоновоеЗадание = ДлительныеОперации.ВыполнитьПроцедуруВНесколькоПотоков(ИмяФункцииИлиПроцедуры, ПараметрыВыполнения, ПараметрыМетода);
		
   	Если ФоновоеЗадание.Статус = "Ошибка" Тогда
		Сообщить(СокрЛП(ФоновоеЗадание.Статус));
	КонецЕсли;

	
	Возврат ФоновоеЗадание;	
	
КонецФункции

&НаСервере
Функция ПараметрыМногопоточнойФункции()
	
	Результат = Новый Структура();
	Результат.Вставить("ТипОбработки", 0);
	Результат.Вставить("ОписаниеСправочника", "");
	Результат.Вставить("КомментарийДокумента", "");
	Результат.Вставить("Организация", Справочники._ДемоОрганизации.ПустаяСсылка());
	Результат.Вставить("ЗагружатьПорциями", Истина);
	Возврат Результат;
	
КонецФункции

Здесь, я обозначаю условные параметры для двух потоков "Поток 1" и "Поток 2". Далее, использую Функцию "ВыполнитьПроцедуруВНесколькоПотоков" с этими параметрами.

Опишем функции "ПрогрессВыполнения" и "ОбработатьДанные" (это будут чисто-условные "полупустые" функции)

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

&НаСервереБезКонтекста
Функция ПрочитатьПрогресс(ИдентификаторЗадания)
	Возврат ДлительныеОперации.ПрочитатьПрогресс(ИдентификаторЗадания);
КонецФункции

&НаКлиенте
Процедура ОбработатьДанные(Задание, ДополнительныеПараметры) Экспорт

	Если Задание = Неопределено Тогда
		Возврат;
	КонецЕсли;  
	
	ЭтаФорма.РезультатВыполнения = СокрЛП(Задание.Статус) +" "+СокрЛП(ПолучитьИЗВременногоХранилища(Задание.АдресРезультата));

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

Весь код модуля формы обработки вот такой (можно скопипастить):

&НаСервере
Функция ПараметрыМногопоточнойФункции()
	
	Результат = Новый Структура();
	Результат.Вставить("ТипОбработки", 0);
	Результат.Вставить("ОписаниеСправочника", "");
	Результат.Вставить("КомментарийДокумента", "");
	Результат.Вставить("Организация", Справочники._ДемоОрганизации.ПустаяСсылка());
	Результат.Вставить("ЗагружатьПорциями", Истина);
	Возврат Результат;
	
КонецФункции	

&НаКлиенте
Процедура ЗапуститьМногопоток(Команда)
	
	ОповещениеОПрогрессеВыполнения = Новый ОписаниеОповещения("ПрогрессВыполнения", ЭтотОбъект);
	
	
	Задание = СоздадимПотокиНаСервере(УникальныйИдентификатор);
	
	//ИдентификаторЗадания = ПолучитьИЗВременногоХранилища(Задание.АдресРезультата).Получить("Поток 1").ИдентификаторЗадания;//Задание.ИдентификаторЗадания;
	
	ИдентификаторЗадания = Задание.ИдентификаторЗадания;
	//Сообщить(ИдентификаторЗадания);
	
	НастройкиОжидания = ДлительныеОперацииКлиент.ПараметрыОжидания(ЭтотОбъект);
	НастройкиОжидания.ВыводитьОкноОжидания = Ложь;
	НастройкиОжидания.ОповещениеОПрогрессеВыполнения = ОповещениеОПрогрессеВыполнения;
	НастройкиОжидания.ВыводитьПрогрессВыполнения = Истина;
	

	Обработчик = Новый ОписаниеОповещения("ОбработатьДанные", ЭтотОбъект);
	ДлительныеОперацииКлиент.ОжидатьЗавершение(Задание, Обработчик, НастройкиОжидания);
	
	
КонецПроцедуры

&НаСервере
Функция СоздадимПотокиНаСервере(УникальныйИдентификатор)

	ПараметрыВыполнения = ДлительныеОперации.ПараметрыВыполненияВФоне(УникальныйИдентификатор);
	ПараметрыВыполнения.НаименованиеФоновогоЗадания = НСтр("ru = 'Выполнение многопоточной функции'");
	ПараметрыВыполнения.ЗапуститьВФоне = Истина;
	
	ПараметрыМетода = Новый Соответствие();

	// ===== первый поток выполнения =====
	
	КодПотока = "Поток 1";
	ПараметрыПотока 						= ПараметрыМногопоточнойФункции();
	ПараметрыПотока.ТипОбработки 			= "Вариант 1";
	ПараметрыПотока.ОписаниеСправочника 	= Объект.ОписаниеСправочника; 
	ПараметрыПотока.КомментарийДокумента 	= Объект.КомментарийДокумента;
	ПараметрыПотока.Организация           	= Объект.Организация;	
	
	ПараметрыВызоваСервера = Новый Массив;
	ПараметрыВызоваСервера.Добавить(КодПотока); 		 // КодПотока
	ПараметрыВызоваСервера.Добавить(ПараметрыПотока);    // Параметры функции в потоке
	
	ПараметрыМетода.Вставить(КодПотока,ПараметрыВызоваСервера); 
	
	// ===== второй поток выполнения =====
	
	КодПотока = "Поток 2";
	ПараметрыПотока 						= ПараметрыМногопоточнойФункции();
	ПараметрыПотока.ТипОбработки 			= "Вариант 2";
	ПараметрыПотока.ОписаниеСправочника 	= Объект.ОписаниеСправочника; 
	ПараметрыПотока.КомментарийДокумента 	= Объект.КомментарийДокумента;
	ПараметрыПотока.Организация           	= Объект.Организация;	
	
	ПараметрыВызоваСервера = Новый Массив;
	ПараметрыВызоваСервера.Добавить(КодПотока); 		 // КодПотока
	ПараметрыВызоваСервера.Добавить(ПараметрыПотока);    // Параметры функции в потоке
	
	ПараметрыМетода.Вставить(КодПотока,ПараметрыВызоваСервера); 	
	
	// =================================== 
	
	ИмяФункцииИлиПроцедуры = "ОбщийМодуль1.ПроцедураМногопоток";
	
	ФоновоеЗадание = ДлительныеОперации.ВыполнитьПроцедуруВНесколькоПотоков(ИмяФункцииИлиПроцедуры, ПараметрыВыполнения, ПараметрыМетода);
		
   	Если ФоновоеЗадание.Статус = "Ошибка" Тогда
		Сообщить(СокрЛП(ФоновоеЗадание.Статус));
	КонецЕсли;

	
	Возврат ФоновоеЗадание;	
	
КонецФункции	

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

&НаСервереБезКонтекста
Функция ПрочитатьПрогресс(ИдентификаторЗадания)
	Возврат ДлительныеОперации.ПрочитатьПрогресс(ИдентификаторЗадания);
КонецФункции

&НаКлиенте
Процедура ОбработатьДанные(Задание, ДополнительныеПараметры) Экспорт

	Если Задание = Неопределено Тогда
		Возврат;
	КонецЕсли;  
	
	ЭтаФорма.РезультатВыполнения = СокрЛП(Задание.Статус) +" "+СокрЛП(ПолучитьИЗВременногоХранилища(Задание.АдресРезультата));
	
КонецПроцедуры

Заключение и выводы

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

Комментарии (3)


  1. vis_inet
    16.12.2022 12:06

    Спасибо.

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


    1. akabrr
      16.12.2022 14:35

      Например обработка большого количества объектов за ограниченное время.


      1. vis_inet
        16.12.2022 19:12

        Это понятно, для разработчика может быть полезно.

        А в типовых конфигурациях где это может быть применимо?