! | Данная информация предназначена только только для IT-специалистов по системной интеграции модулей БИОСОФТ-М. (см. Руководства пользователя к программным продуктам) |
Иллюстрации к документации в ParaService для параллельных задач
не влезающие уже туда:
Сначала см. общие принципы в ParaService Programmer Guide
Наиболее современная структура классов заложена в Absex - Параллельные процессы в шаблоне нового модуля и этот пример устарел.
Создаем инкапсуляторы ролей процесса
- CUportParamodClientIface - для вызова с целью получения прикладной функциональности и фабрик объектов ее реализующих независимо от того буду ли они отработаны inproc или вызовами в паралельный процесс (что решает Impl этого класса приватно).
- CUportParamodServiceIface - отвечает приложению является ли оно парасервисом и если да то реализует системную функциональность требуемую только в сервисном прцессе..
Рекомендуется сначала вынести все прикладные функции, выполняемые сервисом в отдельный пакет. Если такого еще нет в приложении. В этом пакете не нагромождать логику коммуникации, запуска и обработки сообщений сервиса. Оставить этот пакет в таком виде, чтобы функции оттуда были доступны приложению и без применения параллельных процессов.
Создать отдельный пакет в котором расположить все классы необходимые как для запуска сервиса со стороны главного приложения и отсылки постов, так и обработчик постов самого сервиса, вызывающий в свою очередь прикладные функции из прикладных пакетов согласно принятым ми директивам от клиентов.
Если функциональность, которую мы хотим распараллелить мы расположили в пакете Packages/Foo, то обслуживание параллельности расположим в Packages/FooParaService. См. рис.
Для выбора реализации прикладных функций в нужном режиме (синхронном или параллельном) в инкапсуляторе для клиента вводим фабрику(и) объектов реализующих задачи либо локально в процессе либо отправляющие post
class CUportParamodClientIface : public object { // Operations // This returns to the main app // - either local implementation residing in the current process // - or parallel implemetation operating via ParaService posting // according to its internal settings. // The app does not need to care about the details! ref<CUportIoIfaceGp> NewUportParamodIo() new virtual auto;
ref<CUportIoIface> CUportParamodClientImpl::OnNewUportParamodIo() { if ( // am I a service process? CUportProject::GGetGlobalInstance(). x_rUportLoader-> x_rUportParamodService-> IsStartedAsUportParamodParaService() || // forced inproc execution in the client process? debslot("Paramod.InProcess?").InputBool()) { return ref<CUportIoInprocIface>(); } else { return ref<CUportParamodIo>(); } }
Таким образом все остальное приложение не вникая в проблемы паралельности обращается к NewUportParamodIo() для получения очередного прикладного объекта.
В данном примере достаточно переключить ключ в дебслот чтобы отключить вызовы сервиса и выполнять все в главном процессе.
Дальше, как, например, реализовано в Uport: есть универсальный Impl для вычитывания данных порта, который выбирает внутренний Impl на основании того объекта, что вернет x_rUportParamodClient->NewUportParamodIo() и дальше все методы маршрутирезируются либо в in-proc реализацию либо в параллельный сервис:
ref<CUportIoIface> CUportIoImpl::GetImpl() { if (!m_bPrivateActualUportIoInitOk) { m_bPrivateActualUportIoInitOk = true; m_rPrivateActualUportIo = CUportProject::GGetGlobalInstance(). x_rUportLoader-> x_rUportParamodClient-> NewUportParamodIo(); } return m_rPrivateActualUportIo; } bool CUportIoImpl::OnIsUportIoInitialized() { return GetImpl()->IsUportIoInitialized(); } int CUportIoImpl::OnGetUnimodPort() { return GetImpl()->GetUnimodPort(); } int CUportIoImpl::OnGetUnimodNode() { return GetImpl()->GetUnimodNode(); } void CUportIoImpl::OnInitUportIo( int iPort) { GetImpl()->InitUportIo(iPort); } str CUportIoImpl::OnGetFullUsbReport() { return GetImpl()->GetFullUsbReport(); } void CUportIoImpl::Onx_nLostPackets_Set( int value) { GetImpl()->x_nLostPackets = value; } ...
) Добавляем в лоадер оба глобальных (и взаимоисключающих) объекта инкапсулирующих вызов парасервиса из клиентского процесса либо инициализирующих его в самом сервисе
class CUportLoaderIface : public object { //VL: 2010-07-22 // ParaService ref<CUportParamodClientIface> x_rUportParamodClient, auto(Get); ref<CUportParamodServiceIface> x_rUportParamodService, auto(Get); //VL.
Такая примитивная глобальная структура будет работать в большинстве случаев.
Усложнение ее может происходить если потребуется запускать N-ное количество сервисных процессов либо если будет реализовано (сейчас не поддерживается) реализация нескольких сервисов одним процессом.
Однако так или иначе в лоадере для данного проекта будут некие корневые контроллеры определяющие принципиально глобальные функции относящиеся к текущему процессу.
Эти проперти приложение использует когда уже знает сервис оно или клиент.
При запуске в режиме сервиса мы не хотим показывать главное окно и инициализировать объекты обслуживающие основное приложение. Поэтому инициализацию сервиса вписывам сюда:
void CStabipLoaderImpl::OnStartApplication() { //VL: 2010-07-04 x_rStabipDizzyService-> TryInitAsStabipDizzyServiceProcess(); if (x_rStabipDizzyService-> IsStartedAsStabipDizzyParaService()) { return; } //VL. // Init main app and show main window...
То есть пытаемся инициализировать инкапсулятор нашего процесса как сервис. Если это удалось то остальную обычную инициализацию приложения и главного окна не делаем.
Общение сервиса с внешним миром будет происходить не посредством его главного окна а путем приема им внешних команд, хандлер которых инициализирован если IsInit() здесь вернул true.
Реализуем методы инициализации процесса как ParaService вызванные выше:
void CStabipDizzyServiceImpl::OnTryInitAsStabipDizzyServiceProcess() { if (_m_bOneTimeInitOk) { return; } _m_bOneTimeInitOk = true; if not_null( ref<CInterprocParaServiceIfaceGp> rInterprocParaService = _m_onpostStabipDizzy. TryOpenParaServicePostOffice( CStabipDizzyPost::C_sStabipDizzyPostId, this)) { _m_bWeAreTheParaServiceProcess = true; // // Init as ParaService // // for example: change process priority: sys::SetProcessPriorityClass( sys::E_ProcessPriority_High); } }
bool CStabipDizzyServiceImpl::OnIsStartedAsStabipDizzyParaService() { rASSERT(_m_bOneTimeInitOk); return _m_bWeAreTheParaServiceProcess; }
Храним следующие данные поддерживающие состояние парасервиса:
class CStabipDizzyServiceImpl : public CStabipDizzyServiceIface { private: bool _m_bOneTimeInitOk = false; bool _m_bWeAreTheParaServiceProcess = false; // This Post handler must adhere to PFN_Handler signature in onpost template<> // This servies as a ParaService handler. void HandleStabipDizzyPost( ref<CStabipDizzyPost> rStabipDizzyPost); onpost<HandleStabipDizzyPost> _m_onpostStabipDizzy;
в классе нашего инкапсулятора сервиса.
Остается реализовать хандлер сообщений получаемых парасервисом от клиентов:
void CStabipDizzyServiceImpl::HandleStabipDizzyPost( ref<CStabipDizzyPost> rStabipDizzyPost) { if (rStabipDizzyPost->x_bStateControl == ) ......... ...... }
Если сервису не очевидно что от него требуется он может использовать данные присланные с постом. Опять же важно не забывать что это НЕ КОМАНДЫ а ОБНОВЛЕНИЕ СОСТОЯНИЯ которое требуется каким то из клиентов от сервиса.
Если сервис отрабатывает последовательность команд то их очередь, упорядочивание конфликтов между клиентами, повторы потеряных команд при рестартах сервиса и т.п. надо реализовывать отдельно существующими средствами через post или буфера разделяемой памяти, управляющей файлы и т.п. что выходит за пределы данного топика.