В современной разработке на платформе 1С:Предприятие все чаще возникают задачи, требующие высокой степени параллелизма и отказоустойчивости. Традиционная линейная модель выполнения кода часто становится узким местом, особенно когда речь идет об интеграциях, массовой рассылке уведомлений или сложных математических расчетах. Одним из эффективных архитектурных подходов для решения таких проблем является модель акторов. Давайте разберем, что это такое и как мы можем применить эту концепцию в рамках ограничений платформы 1С.
Проанализируем фундаментальные основы. Модель акторов — это математическая модель параллельных вычислений, в которой универсальной сущностью является «актор». В отличие от классического объектно-ориентированного программирования, где объекты вызывают методы друг друга напрямую (синхронно), акторы взаимодействуют исключительно через передачу сообщений.
Основные свойства актора:
Рассмотрим ситуацию: нам нужно отправить отчеты 1000 контрагентам по электронной почте. Если делать это в одном потоке, ошибка при отправке десятого письма может прервать весь процесс, а общее время выполнения будет критически долгим. Используя модель акторов, мы можем создать «почтовый ящик» (буфер), куда быстро запишем задания, и запустить множество независимых «акторов-обработчиков», которые будут разбирать эти сообщения параллельно.
Выясним, какими объектами метаданных можно имитировать поведение акторов. Поскольку в 1С нет встроенной нативной поддержки акторов (как, например, в языках Erlang или Scala), мы будем использовать комбинацию существующих механизмов.
1. Почтовый ящик (Mailbox) — Регистр сведений
Для хранения сообщений идеально подходит РегистрСведений. Он обеспечивает долговечность (сообщения не пропадут при перезагрузке сервера) и позволяет эффективно выбирать данные. Создадим регистр ОчередьЗадачАкторов со следующей структурой:
ИдентификаторСообщения (УникальныйИдентификатор).ДанныеСообщения (ХранилищеЗначения), Состояние (Перечисление: Новый, ВРаботе, Ошибка).ДатаСоздания, КоличествоПопыток, ТекстОшибки.2. Актор (Исполнитель) — Фоновое задание
Роль вычислительной сущности берет на себя ФоновоеЗадание. Оно обеспечивает изоляцию памяти и параллельное выполнение кода.
3. Диспетчер — Регламентное задание
Чтобы система работала автономно, нам нужно РегламентноеЗадание, которое будет периодически проверять наличие новых записей в регистре и инициировать запуск фоновых заданий-акторов.
Рассмотрим пример кода для отправки сообщения актору. Мы не вызываем функцию обработки напрямую, а лишь фиксируем намерение в буфере:
Процедура ОтправитьСообщениеАктору(АдресПолучателя, Тема, Текст) Экспорт
НоваяЗапись = РегистрыСведений.ОчередьЗадачАкторов.СоздатьМенеджерЗаписи();
НоваяЗапись.ИдентификаторСообщения = Новый УникальныйИдентификатор();
СтруктураДанных = Новый Структура;
СтруктураДанных.Вставить("Кому", АдресПолучателя);
СтруктураДанных.Вставить("Тема", Тема);
СтруктураДанных.Вставить("Тело", Текст);
НоваяЗапись.ДанныеСообщения = Новый ХранилищеЗначения(СтруктураДанных);
НоваяЗапись.Состояние = Перечисления.СостоянияЗадач.Новый;
НоваяЗапись.ДатаСоздания = ТекущаяДатаСеанса();
НоваяЗапись.Записать();
КонецПроцедуры
Далее проанализируем работу диспетчера, который запускает «акторов». Важно ограничить количество одновременно работающих потоков, чтобы не перегрузить сервер приложений:
Процедура ОбработкаОчередиАкторов() Экспорт
// Выбираем порцию новых сообщений
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 10
| Очередь.ИдентификаторСообщения КАК ID
|ИЗ
| РегистрСведений.ОчередьЗадачАкторов КАК Очередь
|ГДЕ
| Очередь.Состояние = ЗНАЧЕНИЕ(Перечисление.СостоянияЗадач.Новый)";
Результат = Запрос.Выполнить().Выгрузить();
Для Каждого Строка ИЗ Результат Цикл
// Запускаем фоновое задание для каждого сообщения (актор)
ПараметрыЗадания = Новый Массив;
ПараметрыЗадания.Добавить(Строка.ID);
ФоновыеЗадания.Выполнить("МодульАкторов.ВыполнитьЗадачу", ПараметрыЗадания);
КонецЦикла;
КонецПроцедуры
В полноценных системах акторов существует понятие «Дерева надзора». Если один актор падает с ошибкой, его родитель должен решить: перезапустить его, остановить или проигнорировать ошибку. В 1С мы можем реализовать это через механизм повторных попыток.
Посмотрим на пример логики внутри самого актора:
Процедура ВыполнитьЗадачу(ИдентификаторСообщения) Экспорт
// Блокируем запись, чтобы другие потоки её не взяли
Менеджер = РегистрыСведений.ОчередьЗадачАкторов.СоздатьМенеджерЗаписи();
Менеджер.ИдентификаторСообщения = ИдентификаторСообщения;
Менеджер.Прочитать();
Если Менеджер.Выбран() Тогда
Менеджер.Состояние = Перечисления.СостоянияЗадач.ВРаботе;
Менеджер.Записать();
Попытка
Данные = Менеджер.ДанныеСообщения.Получить();
// Здесь выполняется реальная работа, например, отправка почты
ОтправитьПочту(Данные.Кому, Данные.Тема, Данные.Тело);
// Если всё успешно - удаляем сообщение (актор выполнил задачу)
Менеджер.Удалить();
Исключение
// Стратегия надзора: увеличиваем счетчик попыток
Менеджер.Состояние = Перечисления.СостоянияЗадач.Ошибка;
Менеджер.ТекстОшибки = ОписаниеОшибки();
Менеджер.КоличествоПопыток = Менеджер.КоличествоПопыток + 1;
Менеджер.Записать();
КонецПопытки;
КонецЕсли;
КонецПроцедуры
Не стоит забывать, что в последних версиях платформы 8.3 реализован механизм СистемаВзаимодействия. Это, по сути, готовая инфраструктура для модели акторов. Мы можем программно создавать «интеграции» (ботов), которые подписываются на сообщения в определенных обсуждениях. Когда в обсуждение падает сообщение, платформа сама инициирует запуск кода обработки. Это гораздо ближе к «чистой» модели акторов, так как исключает необходимость ручного управления очередями в регистрах сведений.
Подводя итог, можно сказать, что хотя 1С не предоставляет акторы «из коробки» как примитив языка, сама архитектура платформы позволяет собрать надежный аналог этой модели. Использование связки РегистрСведений + ФоновыеЗадания превращает вашу конфигурацию в мощную систему, способную обрабатывать тысячи параллельных процессов без потери контроля над состоянием системы.