Погружение в SObjectizer 5.5. Вводная часть
-
Upload
yauheni-akhotnikau -
Category
Software
-
view
210 -
download
8
Transcript of Погружение в SObjectizer 5.5. Вводная часть
Погружение в SObjectizer-5.5
SObjectizer Team, февраль 2015
Вводная часть
SObjectizer Team, февраль 2015
О чем пойдет речь?
О чем пойдет речь?
Об инструменте длямногопоточного программирования.
SObjectizer Team, февраль 2015
О чем пойдет речь?
Об инструменте длямногопоточного программирования.
Которое считается сложным...
SObjectizer Team, февраль 2015
О чем пойдет речь?
Об инструменте длямногопоточного программирования.
Которое считается сложным.Не просто сложным,
SObjectizer Team, февраль 2015
О чем пойдет речь?
Об инструменте длямногопоточного программирования
Которое считается сложным.Не просто сложным,а ОЧЕНЬ СЛОЖНЫМ занятием.
SObjectizer Team, февраль 2015
Ведь постоянно иметь дело cнизкоуровневыми примитивами вроде:
SObjectizer Team, февраль 2015
Ведь постоянно иметь дело cнизкоуровневыми примитивами вроде:
● native threads● critical sections, semaphores, mutexes, condition
variables, monitors● thread local storages● atomic variables, spinlocks...
SObjectizer Team, февраль 2015
Ведь постоянно иметь дело с низкоуровневыми примитивами вроде:
● native threads● critical sections, semaphores, mutexes, condition variables,
monitors● thread local storages● atomic variables, spinlocks...
И последствиями их применения в виде тупиков и гонок - это же очень сложно...
SObjectizer Team, февраль 2015
Ведь постоянно иметь дело cнизкоуровневыми примитивами вроде:
● native threads● critical sections, semaphores, mutexes, condition variables,
monitors● thread local storages● atomic variables, spinlocks...
И последствиями их применения в видетупиков и гонок - это же очень сложно...
Правильно?SObjectizer Team, февраль 2015
Правильно!
SObjectizer Team, февраль 2015
Правильно!
Однако, при использовании
SObjectizer...
SObjectizer Team, февраль 2015
SObjectizer Team, февраль 2015
Мы практически ни с чем из перечисленного
не сталкиваемся
SObjectizer позволяет организоватьмногопоточное приложение в видесовокупности объектов-агентов.
SObjectizer Team, февраль 2015
SObjectizer позволяет организоватьмногопоточное приложение в видесовокупности объектов-агентов.
SObjectizer Team, февраль 2015
Взаимодействующих друг с другомтолько посредством
асинхронных сообщений.
Каждый агент получает свой собственный контекст, на котором выполняет обработку своих сообщений.
SObjectizer Team, февраль 2015
Каждый агент получает свой собственный контекст, на котором выполняет обработку своих сообщений.
Агент привязан к своему контексту и может не заботиться о защите своих данных в многопоточном окружении.
SObjectizer Team, февраль 2015
Каждый агент получает свой собственный контекст, на котором выполняет обработку своих сообщений.
Агент привязан к своему контексту и может не заботиться о защите своих данных в многопоточном окружении.
Эта защита выполняется автоматическисамим SObjectizer-ом!
SObjectizer Team, февраль 2015
Да!
SObjectizer Team, февраль 2015
Да! Это очень напоминает Actor Model
SObjectizer Team, февраль 2015
Да! Это очень напоминает Actor Model
Потому что SObjectizer создавалсяи под ее влиянием...
SObjectizer Team, февраль 2015
Да! Это очень напоминает Actor Model
Потому что SObjectizer создавалсяи под ее влиянием...
При этом SObjectizer - это небольшая C++ библиотека, в зону ответственности которой входят:
SObjectizer Team, февраль 2015
Да! Это очень напоминает Actor Model
Потому что SObjectizer создавалсяи под ее влиянием...
При этом SObjectizer - это небольшая C++ библиотека, в зону ответственности которой входят:
SObjectizer Team, февраль 2015
● доставка сообщений внутри одного процесса и
Да! Это очень напоминает Actor Model
Потому что SObjectizer создавалсяи под ее влиянием...
При этом SObjectizer - это небольшая C++ библиотека, в зону ответственности которой входят:
SObjectizer Team, февраль 2015
● доставка сообщений внутри одного процесса и● предоставление рабочего контекста агентам, плюс
Да! Это очень напоминает Actor Model
Потому что SObjectizer создавалсяи под ее влиянием...
При этом SObjectizer - это небольшая C++ библиотека, в зону ответственности которой входят:
SObjectizer Team, февраль 2015
● доставка сообщений внутри одного процесса и● предоставление рабочего контекста агентам, плюс● тонкая настройка всего этого дела
SObjectizer Team, февраль 2015
У нас не было задачи создатьеще один Erlang,
со своим компилятором,стандартными библиотеками и
средой исполнения
SObjectizer вообще родился тогда, когда за пределами Ericsson о Erlang мало кто знал.
SObjectizer Team, февраль 2015
SObjectizer вообще родился тогда, когда за пределами Ericsson о Erlang мало кто знал.
SObjectizer Team, февраль 2015
В 1996-м в Гомельском КБ Системного Программирования появилась первая версия SCADA Objectizer.
SObjectizer вообще родился тогда, когда за пределами Ericsson о Erlang мало кто знал.
SObjectizer Team, февраль 2015
В 1996-м в Гомельском КБ Системного Программирования появилась перваяверсия SCADA Objectizer.
Это был инструмент для задач АСУ ТП,в котором агенты отсылали друг другу
асинхронные сообщения.
SObjectizer вообще родился тогда, когда за пределами Ericsson о Erlang мало кто знал.
SObjectizer Team, февраль 2015
В 1996-м в Гомельском КБ Системного Программирования появилась перваяверсия SCADA Objectizer.
Это был инструмент для задач АСУ ТП,в котором агенты отсылали друг другу
асинхронные сообщения.
А контекст для их обработки обеспечивался диспетчером.
В 2000-м разработка SCADA Objectizer прекратилась, коллектив распался.
Но идеи об агентах, обрабатывающих асинхронные сообщения под управлением диспетчера, - остались.
SObjectizer Team, февраль 2015
В 2000-м разработка SCADA Objectizer прекратилась, коллектив распался.
Но идеи об агентах, обрабатывающих асинхронные сообщения под управлением диспетчера, - остались.
SObjectizer Team, февраль 2015
Поэтому, когда в 2001-м уже в компании Интервэйл бывшие разработчики SCADA
Objectizer реализовали многопоточное GUI-приложение, работающее с внешним
оборудованием...
SObjectizer Team, февраль 2015
...то оказалось, что в нем живет примитивная и сильно урезанная версия SCADA Objectizer
SObjectizer Team, февраль 2015
...то оказалось, что в нем живет примитивная и сильно урезанная версия SCADA Objectizer
Что дало толчок к разработке нового инструмента, построенного на тех же идеях, но уже имеющего новую реализацию...
SObjectizer Team, февраль 2015
...то оказалось, что в нем живет примитивная и сильно урезанная версия SCADA Objectizer
Что дало толчок к разработке нового инструмента, построенного на тех же идеях, но уже имеющего новую реализацию...
Так в 2002-м появился SObjectizer.
SObjectizer Team, февраль 2015
Долгое время SObjectizer был внутренним инструментом компании Интервэйл.
SObjectizer Team, февраль 2015
Долгое время SObjectizer был внутренним инструментом компании Интервэйл.
Использовался в разработке систем:
SObjectizer Team, февраль 2015
Долгое время SObjectizer был внутренним инструментом компании Интервэйл.
Использовался в разработке систем:● передачи SMS/USSD трафика;
SObjectizer Team, февраль 2015
Долгое время SObjectizer был внутренним инструментом компании Интервэйл.
Использовался в разработке систем:● передачи SMS/USSD трафика;● обслуживания финансовых транзакций;
SObjectizer Team, февраль 2015
Долгое время SObjectizer был внутренним инструментом компании Интервэйл.
Использовался в разработке систем:● передачи SMS/USSD трафика;● обслуживания финансовых транзакций;● мониторинга параметров ПО.
SObjectizer Team, февраль 2015
Долгое время SObjectizer был внутренним инструментом компании Интервэйл.
Использовался в разработке систем:● передачи SMS/USSD трафика;● обслуживания финансовых транзакций;● мониторинга параметров ПО;
В 2006-м открыт на SourceForge как OpenSource проект под BSD-лицензией.
SObjectizer Team, февраль 2015
Долгое время SObjectizer был внутренним инструментом компании Интервэйл.
Использовался в разработке систем:● передачи SMS/USSD трафика;● обслуживания финансовых транзакций;● мониторинга параметров ПО.
В 2006-м открыт на SourceForge как OpenSource проект под BSD-лицензией.
С 2013-го развивается на SourceForge полностью как самостоятельный,
независимый от Интервэйл проект.
SObjectizer Team, февраль 2015
За время своего существования SObjectizer несколько раз полностью переписывался.
SObjectizer Team, февраль 2015
За время своего существования SObjectizer несколько раз полностью переписывался.
Далее речь пойдет о пятом поколении SObjectizer, разработка которого началась в 2010-м.
SObjectizer Team, февраль 2015
За время своего существования SObjectizer несколько раз полностью переписывался.
Далее речь пойдет о пятом поколении SObjectizer, разработка которого началась в 2010-м.
А более конкретно - о версии 5.5.3, выпущеннойв феврале 2015-го.
SObjectizer Team, февраль 2015
Вся работа в SObjectizer происходит внутриSObjectizer Environment.
SObjectizer Team, февраль 2015
Вся работа в SObjectizer происходит внутриSObjectizer Environment.
SObjectizer Environment - это контейнер, содержащий SObjectizer Run-Time, кооперации агентов, почтовые ящики для обмена сообщениями, диспетчеры и таймерную нить.
SObjectizer Team, февраль 2015
Вся работа в SObjectizer происходит внутриSObjectizer Environment.
SObjectizer Environment - это контейнер, содержащий SObjectizer Run-Time, кооперации агентов, почтовые ящики для обмена сообщениями, диспетчеры и таймерную нить.
Можно создать несколько экземпляровSObjectizer Environment. Каждый из них будет
работать независимо от других.
SObjectizer Team, февраль 2015
SObjectizer Environment создается функциейso_5::launch().
SObjectizer Team, февраль 2015
SObjectizer Environment создается функциейso_5::launch().
Внутри Environment so_5::launch() запускает экземпляр SObjectizer Run-Time и возвращает управление после его останова.
SObjectizer Team, февраль 2015
SObjectizer Environment создается функциейso_5::launch().
Внутри Environment so_5::launch() запускает экземпляр SObjectizer Run-Time и возвращает управление после его останова.
Что именно будет происходить внутри запущенного Run-Time определяется пользовательской стартовой функцией.
SObjectizer Team, февраль 2015
SObjectizer Environment создается функциейso_5::launch().
Внутри Environment so_5::launch() запускает экземпляр SObjectizer Run-Time и возвращает управление после его останова.Что именно будет происходить внутри запущенного Run-Time определяется пользовательской стартовой функцией.
В случае ошибок so_5::launch() выбрасывает исключения.
SObjectizer Team, февраль 2015
Выглядит это так:#include <iostream>
#include <so_5/all.hpp>
void init( so_5::rt::environment_t & env ) { ... }
int main(){ try { so_5::launch( &init ); } catch( const std::exception & x ) { std::cerr << "Exception: " << x.what() << std::endl; }}
SObjectizer Team, февраль 2015
Выглядит это так:#include <iostream>
#include <so_5/all.hpp>
void init( so_5::rt::environment_t & env ) { ... }
int main(){ try { so_5::launch( &init ); } catch( const std::exception & x ) { std::cerr << "Exception: " << x.what() << std::endl; }}
Главный заголовочный файл со всеми нужными определениями.
SObjectizer Team, февраль 2015
Выглядит это так:#include <iostream>
#include <so_5/all.hpp>
void init( so_5::rt::environment_t & env ) { ... }
int main(){ try { so_5::launch( &init ); } catch( const std::exception & x ) { std::cerr << "Exception: " << x.what() << std::endl; }}
Стартовая функция, которая отвечает за запуск прикладной логики приложения.
SObjectizer Team, февраль 2015
Выглядит это так:#include <iostream>
#include <so_5/all.hpp>
void init( so_5::rt::environment_t & env ) { ... }
int main(){ try { so_5::launch( &init ); } catch( const std::exception & x ) { std::cerr << "Exception: " << x.what() << std::endl; }}
Созданный экземпляр Environment, внутри которого будет работать стартовая функция и все, что в этой функции будет порождено.
SObjectizer Team, февраль 2015
Выглядит это так:#include <iostream>
#include <so_5/all.hpp>
void init( so_5::rt::environment_t & env ) { ... }
int main(){ try { so_5::launch( &init ); } catch( const std::exception & x ) { std::cerr << "Exception: " << x.what() << std::endl; }}
Порождение Environment, запуск Run-Time и вызов стартовой функции init. Возврат из launch() произойдет когда завершат работу созданные внутри init() прикладные агенты.
SObjectizer Team, февраль 2015
Выглядит это так:#include <iostream>
#include <so_5/all.hpp>
void init( so_5::rt::environment_t & env ) { ... }
int main(){ try { so_5::launch( &init ); } catch( const std::exception & x ) { std::cerr << "Exception: " << x.what() << std::endl; }}
Обработка ошибок, информирование о которых выполняется посредством исключений.
SObjectizer Team, февраль 2015
Внутри стартовой функции обычно создается одна или несколько коопераций с прикладными агентами.
SObjectizer Team, февраль 2015
Внутри стартовой функции обычно создается одна или несколько коопераций с прикладными агентами.
Кооперация - это группа агентов, которые должны работать вместе и которые не могут существовать друг без друга.
SObjectizer Team, февраль 2015
Внутри стартовой функции обычно создается одна или несколько коопераций с прикладными агентами.
Кооперация - это группа агентов, которые должны работать вместе и которые не могут существовать друг без друга.
Например, агенты pinger и ponger, которые обмениваются друг с другом сообщениями ping и pong.
SObjectizer Team, февраль 2015
Внутри стартовой функции обычно создается одна или несколько коопераций с прикладными агентами.
Кооперация - это группа агентов, которые должны работать вместе и которые не могут существовать друг без друга.
Например, агенты pinger и ponger, которые обмениваются друг с другом сообщениями ping и pong.
Нет смысла отдельно в pinger-е и отдельно в ponger-е. Эти два агента должныпоявляться и исчезать одновременно.
SObjectizer Team, февраль 2015
Внутри стартовой функции обычно создается одна или несколько коопераций с прикладными агентами.
Кооперация - это группа агентов, которые должны работать вместе и которые не могут существовать друг без друга.
Например, агенты pinger и ponger, которые обмениваются друг с другом сообщениями ping и pong.Нет смысла отдельно в pinger-е и отдельно в ponger-е. Эти два агента должныпоявляться и исчезать одновременно.
Именно для этого и нужны кооперации!
SObjectizer Team, февраль 2015
Создание кооперации с двумя агентами:
void init( so_5::rt::environment_t & env ){ auto coop = env.create_coop( so_5::autoname );
coop->add_agent( new pinger( env ) ); coop->add_agent( new ponger( env ) );
env.register_coop( std::move( coop ) );}
SObjectizer Team, февраль 2015
Создание кооперации с двумя агентами:
void init( so_5::rt::environment_t & env ){ auto coop = env.create_coop( so_5::autoname );
coop->add_agent( new pinger( env ) ); coop->add_agent( new ponger( env ) );
env.register_coop( std::move( coop ) );}
Создание кооперации происходит в три этапа.
Сначала Environment создает экземпляр кооперации...
SObjectizer Team, февраль 2015
Создание кооперации с двумя агентами:
void init( so_5::rt::environment_t & env ){ auto coop = env.create_coop( so_5::autoname );
coop->add_agent( new pinger( env ) ); coop->add_agent( new ponger( env ) );
env.register_coop( std::move( coop ) );}
Затем кооперация наполняется агентами...
SObjectizer Team, февраль 2015
Создание кооперации с двумя агентами:
void init( so_5::rt::environment_t & env ){ auto coop = env.create_coop( so_5::autoname );
coop->add_agent( new pinger( env ) ); coop->add_agent( new ponger( env ) );
env.register_coop( std::move( coop ) );}
Затем кооперация регистрируется.
SObjectizer Team, февраль 2015
Создание кооперации с двумя агентами:
void init( so_5::rt::environment_t & env ){ auto coop = env.create_coop( so_5::autoname );
coop->add_agent( new pinger( env ) ); coop->add_agent( new ponger( env ) );
env.register_coop( std::move( coop ) );}
У каждой кооперации должно быть уникальное имя, корректность которого проверяется внутри register_coop().
Но можно попросить SObjectizer самостоятельно выбрать имя для новой кооперации.
SObjectizer Team, февраль 2015
Создание кооперации с двумя агентами:
void init( so_5::rt::environment_t & env ){ auto coop = env.create_coop( so_5::autoname );
coop->add_agent( new pinger( env ) ); coop->add_agent( new ponger( env ) );
env.register_coop( std::move( coop ) );}
Создав кооперацию, стартовая функция завершает свою работу.
Но Environment продолжит работать до тех пор, пока эта кооперация не будет дерегистрирована. Либо пока не поступит команда на останов работы Environment-а.
SObjectizer Team, февраль 2015
Что из себя представляет агент?
SObjectizer Team, февраль 2015
Что из себя представляет агент?
Начнем с самого простого агента в этом примере.
С агента ponger.
SObjectizer Team, февраль 2015
Что из себя представляет агент?
Начнем с самого простого агента в этом примере.
С агента ponger.
Его задача очень проста:● получать сообщения ping;● отвечать сообщениями pong.
SObjectizer Team, февраль 2015
Что из себя представляет агент?
Начнем с самого простого агента в этом примере.
С агента ponger.
Его задача очень проста:● получать сообщения ping;● отвечать сообщениями pong.
Но сперва определим сообщения ping и pong...
SObjectizer Team, февраль 2015
Определение сообщений:struct ping : public so_5::rt::message_t{ unsigned int m_req;
ping( unsigned int req ) : m_req{ req } {}};
struct pong : public so_5::rt::message_t{ unsigned int m_resp;
pong( unsigned int resp ) : m_resp{ resp } {}};
SObjectizer Team, февраль 2015
Определение сообщений:struct ping : public so_5::rt::message_t{ unsigned int m_req;
ping( unsigned int req ) : m_req{ req } {}};
struct pong : public so_5::rt::message_t{ unsigned int m_resp;
pong( unsigned int resp ) : m_resp{ resp } {}};
Каждое сообщение должно быть представлено своим собственным C++ классом (структурой).
На основании информации о типе сообщения затем происходит диспетчеризация сообщений и выбор обработчиков сообщений.
SObjectizer Team, февраль 2015
Определение сообщений:struct ping : public so_5::rt::message_t{ unsigned int m_req;
ping( unsigned int req ) : m_req{ req } {}};
struct pong : public so_5::rt::message_t{ unsigned int m_resp;
pong( unsigned int resp ) : m_resp{ resp } {}};
Все сообщения, которые переносят какие-либо данные внутри себя, должны быть унаследованы от общего базового типаso_5::rt::message_t.
Бывают еще и сообщения, которые информацию внутри не переносят.Это сигналы. Подробнее они рассматриваются ниже.
SObjectizer Team, февраль 2015
Определение сообщений:struct ping : public so_5::rt::message_t{ unsigned int m_req;
ping( unsigned int req ) : m_req{ req } {}};
struct pong : public so_5::rt::message_t{ unsigned int m_resp;
pong( unsigned int resp ) : m_resp{ resp } {}};
SObjectizer не налагает каких-либо серьезных ограничений на то, что находится внутри сообщений.
В данном случае поля m_req и m_resp нужны только для работы демонстрационного примера. К особенностям SObjectizer они отношения не имеют.
SObjectizer Team, февраль 2015
Агент ponger:class ponger : public so_5::rt::agent_t{public : ponger( so_5::rt::environment_t & env ) : so_5::rt::agent_t( env ) , m_table( env.create_local_mbox( "table" ) ) {}
virtual void so_define_agent() override { so_default_state().event( m_table, &ponger::evt_ping ); }
private : const so_5::rt::mbox_t m_table;
void evt_ping( const ping & evt ) { so_5::send< pong >( m_table, evt.m_req ); }};
SObjectizer Team, февраль 2015
Агент ponger:class ponger : public so_5::rt::agent_t{public : ponger( so_5::rt::environment_t & env ) : so_5::rt::agent_t( env ) , m_table( env.create_local_mbox( "table" ) ) {}
virtual void so_define_agent() override { so_default_state().event( m_table, &ponger::evt_ping ); }
private : const so_5::rt::mbox_t m_table;
void evt_ping( const ping & evt ) { so_5::send< pong >( m_table, evt.m_req ); }};
Каждый обычный агент должен иметь свой собственный C++ класс.
Могут быть еще и необычные, т.н. ad-hoc-агенты. О них речь пойдет чуть позже.
SObjectizer Team, февраль 2015
Агент ponger:class ponger : public so_5::rt::agent_t{public : ponger( so_5::rt::environment_t & env ) : so_5::rt::agent_t( env ) , m_table( env.create_local_mbox( "table" ) ) {}
virtual void so_define_agent() override { so_default_state().event( m_table, &ponger::evt_ping ); }
private : const so_5::rt::mbox_t m_table;
void evt_ping( const ping & evt ) { so_5::send< pong >( m_table, evt.m_req ); }};
Класс каждого обычного агента должен наследоваться от общего базового типа,so_5::rt::agent_t.
SObjectizer Team, февраль 2015
Агент ponger:class ponger : public so_5::rt::agent_t{public : ponger( so_5::rt::environment_t & env ) : so_5::rt::agent_t( env ) , m_table( env.create_local_mbox( "table" ) ) {}
virtual void so_define_agent() override { so_default_state().event( m_table, &ponger::evt_ping ); }
private : const so_5::rt::mbox_t m_table;
void evt_ping( const ping & evt ) { so_5::send< pong >( m_table, evt.m_req ); }};
Каждый агент должен быть связан с тем Environment-ом, в котором агенту предстоит работать.
Поэтому ссылка на Environment должна передаваться в конструктор агента. А оттуда - в конструктор базового типа,so_5::rt::agent_t.
SObjectizer Team, февраль 2015
Агент ponger:class ponger : public so_5::rt::agent_t{public : ponger( so_5::rt::environment_t & env ) : so_5::rt::agent_t( env ) , m_table( env.create_local_mbox( "table" ) ) {}
virtual void so_define_agent() override { so_default_state().event( m_table, &ponger::evt_ping ); }
private : const so_5::rt::mbox_t m_table;
void evt_ping( const ping & evt ) { so_5::send< pong >( m_table, evt.m_req ); }};
Перед тем, как агент будет зарегистрирован в составе кооперации, SObjectizer вызовет у него метод so_define_agent().В этом методе агент должен выполнить все необходимые настройки.В частности, подписаться на интересующие его сообщения.
SObjectizer Team, февраль 2015
Агент ponger:class ponger : public so_5::rt::agent_t{public : ponger( so_5::rt::environment_t & env ) : so_5::rt::agent_t( env ) , m_table( env.create_local_mbox( "table" ) ) {}
virtual void so_define_agent() override { so_default_state().event( m_table, &ponger::evt_ping ); }
private : const so_5::rt::mbox_t m_table;
void evt_ping( const ping & evt ) { so_5::send< pong >( m_table, evt.m_req ); }};
Агент ponger подписывается только на одно сообщение. Это сообщение типа ping, которое приходит из почтового ящика с именем “table”.Почтовый ящик при подписке должен указываться явно.А вот тип сообщения SObjectizer определяет сам по сигнатуре обработчика сообщения.
SObjectizer Team, февраль 2015
Агент ponger:class ponger : public so_5::rt::agent_t{public : ponger( so_5::rt::environment_t & env ) : so_5::rt::agent_t( env ) , m_table( env.create_local_mbox( "table" ) ) {}
virtual void so_define_agent() override { so_default_state().event( m_table, &ponger::evt_ping ); }
private : const so_5::rt::mbox_t m_table;
void evt_ping( const ping & evt ) { so_5::send< pong >( m_table, evt.m_req ); }};
Метод, в котором агент обрабатывает сообщение, называется методом-обработчиком события. Или просто событием.
SObjectizer Team, февраль 2015
Агент ponger:class ponger : public so_5::rt::agent_t{public : ponger( so_5::rt::environment_t & env ) : so_5::rt::agent_t( env ) , m_table( env.create_local_mbox( "table" ) ) {}
virtual void so_define_agent() override { so_default_state().event( m_table, &ponger::evt_ping ); }
private : const so_5::rt::mbox_t m_table;
void evt_ping( const ping & evt ) { so_5::send< pong >( m_table, evt.m_req ); }};
Породившее событие сообщение передается в метод-обработчик по константной ссылке.Метод-обработчик не должен модифицировать переданный ему экземпляр сообщения, т.к. этот же экземпляр в данный момент могут обрабатывать и другие агенты.
SObjectizer Team, февраль 2015
Агент ponger:class ponger : public so_5::rt::agent_t{public : ponger( so_5::rt::environment_t & env ) : so_5::rt::agent_t( env ) , m_table( env.create_local_mbox( "table" ) ) {}
virtual void so_define_agent() override { so_default_state().event( m_table, &ponger::evt_ping ); }
private : const so_5::rt::mbox_t m_table;
void evt_ping( const ping & evt ) { so_5::send< pong >( m_table, evt.m_req ); }};
На сообщение ping агент отвечает отсылкой сообщения pong в почтовый ящик с именем “table”.
SObjectizer Team, февраль 2015
Агент ponger:class ponger : public so_5::rt::agent_t{public : ponger( so_5::rt::environment_t & env ) : so_5::rt::agent_t( env ) , m_table( env.create_local_mbox( "table" ) ) {}
virtual void so_define_agent() override { so_default_state().event( m_table, &ponger::evt_ping ); }
private : const so_5::rt::mbox_t m_table;
void evt_ping( const ping & evt ) { so_5::send< pong >( m_table, evt.m_req ); }};
Функция so_5::send конструирует объект типа pong и отсылает его в указанный почтовый ящик.
SObjectizer Team, февраль 2015
Агент ponger:class ponger : public so_5::rt::agent_t{public : ponger( so_5::rt::environment_t & env ) : so_5::rt::agent_t( env ) , m_table( env.create_local_mbox( "table" ) ) {}
virtual void so_define_agent() override { so_default_state().event( m_table, &ponger::evt_ping ); }
private : const so_5::rt::mbox_t m_table;
void evt_ping( const ping & evt ) { so_5::send< pong >( m_table, evt.m_req ); }};
Дополнительные аргументы so_5::send(), которые следуют за ссылкой на почтовый ящик, передаются в конструктор объекта сообщения.В данном случае это аргумент resp для конструктора pong.
SObjectizer Team, февраль 2015
Агент ponger:class ponger : public so_5::rt::agent_t{public : ponger( so_5::rt::environment_t & env ) : so_5::rt::agent_t( env ) , m_table( env.create_local_mbox( "table" ) ) {}
virtual void so_define_agent() override { so_default_state().event( m_table, &ponger::evt_ping ); }
private : const so_5::rt::mbox_t m_table;
void evt_ping( const ping & evt ) { so_5::send< pong >( m_table, evt.m_req ); }};
Агент ponger самостоятельно получает ссылку на почтовый ящик, который нужен для обмена сообщениями.В данном случае это ящик с именем “table”, который создается посредством вызова create_local_mbox().Ссылка на почтовый ящик сохраняется внутри агента.
SObjectizer Team, февраль 2015
Агент pinger (начало):class pinger : public so_5::rt::agent_t{public : pinger( so_5::rt::environment_t & env ) : so_5::rt::agent_t( env ) , m_table( env.create_local_mbox( "table" ) ) {}
virtual void so_define_agent() override { so_default_state().event( m_table, &pinger::evt_pong ); }
virtual void so_evt_start() override { so_5::send< ping >( m_table, 500 ); }
SObjectizer Team, февраль 2015
Агент pinger (начало):class pinger : public so_5::rt::agent_t{public : pinger( so_5::rt::environment_t & env ) : so_5::rt::agent_t( env ) , m_table( env.create_local_mbox( "table" ) ) {}
virtual void so_define_agent() override { so_default_state().event( m_table, &pinger::evt_pong ); }
virtual void so_evt_start() override { so_5::send< ping >( m_table, 500 ); }
Агент pinger очень похож на агента ponger-а: получает ссылку на Environment в конструкторе, создает ссылку на почтовый ящик с именем “table” и подписывается всего на одно сообщение в so_define_agent().
SObjectizer Team, февраль 2015
Агент pinger (начало):class pinger : public so_5::rt::agent_t{public : pinger( so_5::rt::environment_t & env ) : so_5::rt::agent_t( env ) , m_table( env.create_local_mbox( "table" ) ) {}
virtual void so_define_agent() override { so_default_state().event( m_table, &pinger::evt_pong ); }
virtual void so_evt_start() override { so_5::send< ping >( m_table, 500 ); }
Но есть одно важное отличие: метод so_evt_start().Этот метод вызывается у агента сразу же после того, как агент и его кооперация будут успешно зарегистрированы.В этом методе агент может выполнить свои начальные действия.В данном случае - отослать первое сообщение ping.
SObjectizer Team, февраль 2015
Агент pinger (окончание):private : const so_5::rt::mbox_t m_table;
void evt_pong( const pong & evt ) { if( evt.m_resp ) so_5::send< ping >( m_table, evt.m_resp - 1 ); else so_deregister_agent_coop_normally(); }};
SObjectizer Team, февраль 2015
Агент pinger (окончание):private : const so_5::rt::mbox_t m_table;
void evt_pong( const pong & evt ) { if( evt.m_resp ) so_5::send< ping >( m_table, evt.m_resp - 1 ); else so_deregister_agent_coop_normally(); }};
В своем событии evt_pong агент pinger либо продолжает обмен сообщениями, отсылая следующий ping.Либо, если все ping-и уже были отосланы, инициирует дерегистрацию кооперации, которой он принадлежит.
SObjectizer Team, февраль 2015
Агент pinger (окончание):private : const so_5::rt::mbox_t m_table;
void evt_pong( const pong & evt ) { if( evt.m_resp ) so_5::send< ping >( m_table, evt.m_resp - 1 ); else so_deregister_agent_coop_normally(); }};
Кооперация может быть дерегистрирована по разным причинам. В данном случае указывается, что дерегистрация выполняется нормально, как это и предполагалось прикладной логикой.
После дерегистрации кооперации с pinger-ом и ponger-ом, других работающих коопераций не останется. Environment завершит свою работу и произойдет возврат из so_5::launch().
SObjectizer Team, февраль 2015
Несколько слов о почтовых ящиках (mbox-ах)...
SObjectizer Team, февраль 2015
Несколько слов о почтовых ящиках (mbox-ах)...
В SObjectizer, в отличии от похожих инструментов, вроде Erlang, Akka или CAF, сообщение отсылается не конкретному агенту(актору), а в почтовый ящик (mbox).
SObjectizer Team, февраль 2015
Несколько слов о почтовых ящиках (mbox-ах)...
В SObjectizer, в отличии от похожих инструментов, вроде Erlang, Akka или CAF, сообщение отсылается не конкретному агенту(актору), а в почтовый ящик (mbox).
За mbox-ом в SObjectizer-е может скрываться как один агент, так и несколько агентов.
А может и ни одного.
SObjectizer Team, февраль 2015
В SObjectizer есть два типа mbox-ов:
SObjectizer Team, февраль 2015
В SObjectizer есть два типа mbox-ов:
Multi-Producers/Multi-Consumers mbox-ы.Похожи на “доски объявлений”. Отосланное в mbox сообщение становится доступным для всех, кто подписан на этот mbox.В примере выше продемонстрирован MPMC-mbox.
SObjectizer Team, февраль 2015
В SObjectizer есть два типа mbox-ов:
Multi-Producers/Multi-Consumers mbox-ы.Похожи на “доски объявлений”. Отосланное в mbox сообщение становится доступным для всех, кто подписан на этот mbox.В примере выше продемонстрирован MPMC-mbox.
Multi-Producers/Single-Consumer mbox-ы.У этих mbox-ов только один подписчик - агент, которому принадлежит MPSC-mbox.
SObjectizer Team, февраль 2015
Чтобы продемонстрировать особенности MPMC-mbox-ов добавим в приведенный пример еще одного агента...
SObjectizer Team, февраль 2015
Чтобы продемонстрировать особенности MPMC-mbox-ов добавим в приведенный пример еще одного агента...
Этот агент будет “слушать” обмен сообщениями между агентами pinger и ponger, подсчитывая количество пересланных сообщений.
SObjectizer Team, февраль 2015
Агент listener:class listener : public so_5::rt::agent_t{public : listener( so_5::rt::environment_t & env ) : so_5::rt::agent_t( env ) , m_table( env.create_local_mbox( "table" ) ) {}
virtual void so_define_agent() override { so_default_state() .event( m_table, [this]( const ping & ) { ++m_pings; } ) .event( m_table, [this]( const pong & ) { ++m_pongs; } ); }
virtual void so_evt_finish() override { std::cout << "result: " << m_pings << "/" << m_pongs << std::endl; }
private : const so_5::rt::mbox_t m_table; unsigned int m_pings = 0; unsigned int m_pongs = 0;};
SObjectizer Team, февраль 2015
Агент listener:class listener : public so_5::rt::agent_t{public : listener( so_5::rt::environment_t & env ) : so_5::rt::agent_t( env ) , m_table( env.create_local_mbox( "table" ) ) {}
virtual void so_define_agent() override { so_default_state() .event( m_table, [this]( const ping & ) { ++m_pings; } ) .event( m_table, [this]( const pong & ) { ++m_pongs; } ); }
virtual void so_evt_finish() override { std::cout << "result: " << m_pings << "/" << m_pongs << std::endl; }
private : const so_5::rt::mbox_t m_table; unsigned int m_pings = 0; unsigned int m_pongs = 0;};
Агенту нужно получать два сообщения. Поэтому он подписывает два своих события.Вместо методов обработчиков используются лямбда-функции. Тип сообщений, на которые производится подписка, выводится автоматически по сигнатуре лямбда-функций.
SObjectizer Team, февраль 2015
Агент listener:class listener : public so_5::rt::agent_t{public : listener( so_5::rt::environment_t & env ) : so_5::rt::agent_t( env ) , m_table( env.create_local_mbox( "table" ) ) {}
virtual void so_define_agent() override { so_default_state() .event( m_table, [this]( const ping & ) { ++m_pings; } ) .event( m_table, [this]( const pong & ) { ++m_pongs; } ); }
virtual void so_evt_finish() override { std::cout << "result: " << m_pings << "/" << m_pongs << std::endl; }
private : const so_5::rt::mbox_t m_table; unsigned int m_pings = 0; unsigned int m_pongs = 0;};
Метод so_evt_finish() является противоположностью метода so_evt_start().Он вызывается у агента непосредственно перед тем, как агент завершит свою работу.В данном случае этот метод используется для выдачи результатов.
SObjectizer Team, февраль 2015
Если теперь добавить listener-а в кооперацию:
SObjectizer Team, февраль 2015
Если теперь добавить listener-а в кооперацию:void init( so_5::rt::environment_t & env ){ auto coop = env.create_coop( so_5::autoname );
coop->add_agent( new pinger( env ) ); coop->add_agent( new ponger( env ) ); coop->add_agent( new listener( env ) );
env.register_coop( std::move( coop ) );}
SObjectizer Team, февраль 2015
Если теперь добавить listener-а в кооперацию:void init( so_5::rt::environment_t & env ){ auto coop = env.create_coop( so_5::autoname );
coop->add_agent( new pinger( env ) ); coop->add_agent( new ponger( env ) ); coop->add_agent( new listener( env ) );
env.register_coop( std::move( coop ) );}
То в конце своей работы пример напечатает:result: 501/501
SObjectizer Team, февраль 2015
Если теперь добавить listener-а в кооперацию:void init( so_5::rt::environment_t & env ){ auto coop = env.create_coop( so_5::autoname );
coop->add_agent( new pinger( env ) ); coop->add_agent( new ponger( env ) ); coop->add_agent( new listener( env ) );
env.register_coop( std::move( coop ) );}
То в конце своей работы пример напечатает:result: 501/501
Т.е. отсылка сообщения в MPMC-mbox - это широковещательная рассылка всем подписчикам.
SObjectizer Team, февраль 2015
В отличии от MPMC-mbox-а, который нужно создавать вручную, MPSC-mbox-ы создаются автоматически для каждого агента.Т.е. у каждого агента есть свой собственный MPSC-mbox, который называется direct_mbox-ом.
SObjectizer Team, февраль 2015
В отличии от MPMC-mbox-а, который нужно создавать вручную, MPSC-mbox-ы создаются автоматически для каждого агента.Т.е. у каждого агента есть свой собственный MPSC-mbox, который называется direct_mbox-ом.
Отправленное в MPSC-mbox сообщение либо отдается владельцу mbox-а на обработку, либо выбрасывается, если владелец на сообщение не подписан.
SObjectizer Team, февраль 2015
В отличии от MPMC-mbox-а, который нужно создавать вручную, MPSC-mbox-ы создаются автоматически для каждого агента.Т.е. у каждого агента есть свой собственный MPSC-mbox, который называется direct_mbox-ом.
Отправленное в MPSC-mbox сообщение либо отдается владельцу mbox-а на обработку, либо выбрасывается, если владелец на сообщение не подписан.
Т.е. если два агента общаются друг с другом через direct_mbox-ы, то никто не может
“прослушать” их общение.
SObjectizer Team, февраль 2015
Однако, смысл существования direct_mbox-ов вовсе не в том, чтобы позволить двум агентам установить “закрытый канал” общения.
SObjectizer Team, февраль 2015
Однако, смысл существования direct_mbox-ов вовсе не в том, чтобы позволить двум агентам установить “закрытый канал” общения.
Direct_mbox-ы заметно эффективнее MPMC-mbox-ов, т.к. диспетчеризация сообщений для direct_mbox-ов гораздо проще и требует меньше внутренних блокировок.
SObjectizer Team, февраль 2015
Однако, смысл существования direct_mbox-ов вовсе не в том, чтобы позволить двум агентам установить “закрытый канал” общения.
Direct_mbox-ы заметно эффективнее MPMC-mbox-ов, т.к. диспетчеризация сообщений для direct_mbox-ов гораздо проще и требует меньше внутренних блокировок.
Поэтому, если прикладной логике не требуется широковещательный обмен сообщениями, то лучше работать посредством direct_mbox-ов.
SObjectizer Team, февраль 2015
В обсуждавшемся выше примере с двумя агентами pinger и ponger широковещательная рассылка не нужна.
SObjectizer Team, февраль 2015
В обсуждавшемся выше примере с двумя агентами pinger и ponger широковещательная рассылка не нужна.
Поэтому переделаем этот пример под работу с direct_mbox-ами.
SObjectizer Team, февраль 2015
В обсуждавшемся выше примере с двумя агентами pinger и ponger широковещательная рассылка не нужна.
Поэтому переделаем этот пример под работу с direct_mbox-ами.
Заодно и выбросив агента listener-а.Пусть pinger и ponger сами ведут подсчет
SObjectizer Team, февраль 2015
В обсуждавшемся выше примере с двумя агентами pinger и ponger широковещательная рассылка не нужна.
Поэтому переделаем этот пример под работу с direct_mbox-ами.
Заодно и выбросив агента listener-а.Пусть pinger и ponger сами ведут подсчет
Ну и поменяв сообщения на сигналы
SObjectizer Team, февраль 2015
Сигналы - это разновидность сообщений, в которых есть лишь факт существования сообщения.
Но нет никаких данных внутри сообщения.
SObjectizer Team, февраль 2015
Сигналы - это разновидность сообщений, в которых есть лишь факт существования сообщения.
Но нет никаких данных внутри сообщения.
Это очень напоминает пересылку атомов в Erlang, когда отсылается
только атом, без какой-либо дополнительной информации.
SObjectizer Team, февраль 2015
При использовании SObjectizer сигналы оказались настолько распространены, что для их поддержки добавлены специальные механизмы.
SObjectizer Team, февраль 2015
При использовании SObjectizer сигналы оказались настолько распространены, что для их поддержки добавлены специальные механизмы.
На уровне API работа с сигналами в чем-то похожа на работу с сообщениями. В чем-то нет.
SObjectizer Team, февраль 2015
Меняем сообщения ping и pong на сигналы...
SObjectizer Team, февраль 2015
Меняем сообщения ping и pong на сигналы...struct ping : public so_5::rt::message_t{ unsigned int m_req;
ping( unsigned int req ) : m_req{ req } {}};
struct pong : public so_5::rt::message_t{ unsigned int m_resp;
pong( unsigned int resp ) : m_resp{ resp } {}};
struct ping : public so_5::rt::signal_t {};
struct pong : public so_5::rt::signal_t {};
SObjectizer Team, февраль 2015
Меняем сообщения ping и pong на сигналы...
Сигналы должны наследоваться отso_5::rt::signal_t и не должны содержать данных.
struct ping : public so_5::rt::message_t{ unsigned int m_req;
ping( unsigned int req ) : m_req{ req } {}};
struct pong : public so_5::rt::message_t{ unsigned int m_resp;
pong( unsigned int resp ) : m_resp{ resp } {}};
struct ping : public so_5::rt::signal_t {};
struct pong : public so_5::rt::signal_t {};
SObjectizer Team, февраль 2015
Меняем агента pinger-а...
SObjectizer Team, февраль 2015
Меняем агента pinger-а (начало):class pinger : public so_5::rt::agent_t{public : pinger( so_5::rt::environment_t & env ) : so_5::rt::agent_t( env ) {}
void set_ponger_mbox( const so_5::rt::mbox_t & mbox ) { m_ponger = mbox; }
virtual void so_define_agent() override { so_default_state().event< pong >( [this]{ ++m_pongs; so_5::send< ping >( m_ponger ); } ); }
SObjectizer Team, февраль 2015
Меняем агента pinger-а (начало):class pinger : public so_5::rt::agent_t{public : pinger( so_5::rt::environment_t & env ) : so_5::rt::agent_t( env ) {}
void set_ponger_mbox( const so_5::rt::mbox_t & mbox ) { m_ponger = mbox; }
virtual void so_define_agent() override { so_default_state().event< pong >( [this]{ ++m_pongs; so_5::send< ping >( m_ponger ); } ); }
direct_mbox становится доступен только после создания агента.Поэтому для связывания pinger-а и ponger-а потребовался отдельный метод, который будет вызываться после создания обоих агентов.
SObjectizer Team, февраль 2015
Меняем агента pinger-а (начало):class pinger : public so_5::rt::agent_t{public : pinger( so_5::rt::environment_t & env ) : so_5::rt::agent_t( env ) {}
void set_ponger_mbox( const so_5::rt::mbox_t & mbox ) { m_ponger = mbox; }
virtual void so_define_agent() override { so_default_state().event< pong >( [this]{ ++m_pongs; so_5::send< ping >( m_ponger ); } ); }
В метод event() передается всего один аргумент: лямбда-функция с обработчиком сигнала. В этом случае event() делает подписку на сигнал, поступающий от direct_mbox-а агента.
SObjectizer Team, февраль 2015
Меняем агента pinger-а (начало):class pinger : public so_5::rt::agent_t{public : pinger( so_5::rt::environment_t & env ) : so_5::rt::agent_t( env ) {}
void set_ponger_mbox( const so_5::rt::mbox_t & mbox ) { m_ponger = mbox; }
virtual void so_define_agent() override { so_default_state().event< pong >( [this]{ ++m_pongs; so_5::send< ping >( m_ponger ); } ); }
При подписке на сигнал нужно явно указывать тип сигнала.Обработчиком сигнала должен быть метод или лямбда-функция без параметров.В отличии от сообщения, нет экземпляра сигнала, поэтому нечего передавать параметром обработчику события.
SObjectizer Team, февраль 2015
Меняем агента pinger-а (окончание): virtual void so_evt_start() override { so_5::send< ping >( m_ponger ); }
virtual void so_evt_finish() override { std::cout << "pongs: " << m_pongs << std::endl; }
private : so_5::rt::mbox_t m_ponger; unsigned int m_pongs = 0;};
SObjectizer Team, февраль 2015
Меняем агента pinger-а (окончание): virtual void so_evt_start() override { so_5::send< ping >( m_ponger ); }
virtual void so_evt_finish() override { std::cout << "pongs: " << m_pongs << std::endl; }
private : so_5::rt::mbox_t m_ponger; unsigned int m_pongs = 0;};
Отсылка сигнала выполняется той же функцией so_5::send(), что и отсылка сообщения.Но после mbox-а получателя больше никаких аргументов не требуется.
SObjectizer Team, февраль 2015
class ponger : public so_5::rt::agent_t{public : ponger( so_5::rt::environment_t & env ) : so_5::rt::agent_t( env ) {}
void set_pinger_mbox( const so_5::rt::mbox_t & mbox ) { m_pinger = mbox; }
virtual void so_define_agent() override { so_default_state().event< ping >( [this]{ ++m_pings; so_5::send< pong >( m_pinger ); } ); }
Аналогичным образом меняется агент ponger (начало):
SObjectizer Team, февраль 2015
virtual void so_evt_finish() override { std::cout << "pings: " << m_pings << std::endl; }
private : so_5::rt::mbox_t m_pinger; unsigned int m_pings = 0;};
Аналогичным образом меняется агент ponger (окончание):
SObjectizer Team, февраль 2015
void init( so_5::rt::environment_t & env ){ auto coop = env.create_coop( so_5::autoname );
auto a_pinger = coop->add_agent( new pinger( env ) ); auto a_ponger = coop->add_agent( new ponger( env ) );
a_pinger->set_ponger_mbox( a_ponger->so_direct_mbox() ); a_ponger->set_pinger_mbox( a_pinger->so_direct_mbox() );
env.register_coop( std::move( coop ) );}
Создание кооперации становится более многословным:
SObjectizer Team, февраль 2015
void init( so_5::rt::environment_t & env ){ auto coop = env.create_coop( so_5::autoname );
auto a_pinger = coop->add_agent( new pinger( env ) ); auto a_ponger = coop->add_agent( new ponger( env ) );
a_pinger->set_ponger_mbox( a_ponger->so_direct_mbox() ); a_ponger->set_pinger_mbox( a_pinger->so_direct_mbox() );
env.register_coop( std::move( coop ) );}
Создание кооперации становится более многословным:
Кроме того, здесь есть ошибка...
SObjectizer Team, февраль 2015
void init( so_5::rt::environment_t & env ){ auto coop = env.create_coop( so_5::autoname );
auto a_pinger = coop->add_agent( new pinger( env ) ); auto a_ponger = coop->add_agent( new ponger( env ) );
a_pinger->set_ponger_mbox( a_ponger->so_direct_mbox() ); a_ponger->set_pinger_mbox( a_pinger->so_direct_mbox() );
env.register_coop( std::move( coop ) );}
Создание кооперации становится более многословным:
Кроме того, здесь есть ошибка...
Никто не остановит этих агентов!Они будут пинговать друг друга постоянно.
SObjectizer Team, февраль 2015
Исправим проблему, добавив еще одного агента, который завершит работу примера через одну секунду...
SObjectizer Team, февраль 2015
Исправим проблему, добавив еще одного агента, который завершит работу примера через одну секунду...
Поскольку агент будет обрабатывать всего одно событие, нет смысла определять отдельный класс для этого агента, переопределять в нем метод so_define_agent() и т.д.
SObjectizer Team, февраль 2015
Исправим проблему, добавив еще одного агента, который завершит работу примера через одну секунду...
Поскольку агент будет обрабатывать всего одно событие, нет смысла определять отдельный класс для этого агента, переопределять в нем метод so_define_agent() и т.д.
Вместо этого создадим ad-hoc-агента.Т.е. агента, описанного “по месту”, без
дополнительных формальностей.
SObjectizer Team, февраль 2015
Ad-hoc-агент для завершения примера через секунду после начала работы:
SObjectizer Team, февраль 2015
Ad-hoc-агент для завершения примера через секунду после начала работы:
struct stop : public so_5::rt::signal_t {};
auto stopper = coop->define_agent();stopper.event< stop >( stopper.direct_mbox(), [&env]{ env.stop(); } );
SObjectizer Team, февраль 2015
Ad-hoc-агент для завершения примера через секунду после начала работы:
struct stop : public so_5::rt::signal_t {};
auto stopper = coop->define_agent();stopper.event< stop >( stopper.direct_mbox(), [&env]{ env.stop(); } );
Сигнал на завершение работы.
SObjectizer Team, февраль 2015
Ad-hoc-агент для завершения примера через секунду после начала работы:
struct stop : public so_5::rt::signal_t {};
auto stopper = coop->define_agent();stopper.event< stop >( stopper.direct_mbox(), [&env]{ env.stop(); } );
Создание ad-hoc-агента.Возвращается дескриптор, через который агента можно настраивать.
SObjectizer Team, февраль 2015
Ad-hoc-агент для завершения примера через секунду после начала работы:
struct stop : public so_5::rt::signal_t {};
auto stopper = coop->define_agent();stopper.event< stop >( stopper.direct_mbox(), [&env]{ env.stop(); } );
Новый агент подписывается на единственный сигнал stop.
SObjectizer Team, февраль 2015
Ad-hoc-агент для завершения примера через секунду после начала работы:
struct stop : public so_5::rt::signal_t {};
auto stopper = coop->define_agent();stopper.event< stop >( stopper.direct_mbox(), [&env]{ env.stop(); } );
Сигнал придет на direct_mbox нового агента.
SObjectizer Team, февраль 2015
Ad-hoc-агент для завершения примера через секунду после начала работы:
struct stop : public so_5::rt::signal_t {};
auto stopper = coop->define_agent();stopper.event< stop >( stopper.direct_mbox(), [&env]{ env.stop(); } );
Обработчик этого сигнала даст приказ SObjectizer Environment завершить работу примера.
Единственная кооперация будет дерегистрирована автоматически.
SObjectizer Team, февраль 2015
Ad-hoc-агент создан и настроен.Осталось отослать отложенный на одну секунду сигнал stop:
SObjectizer Team, февраль 2015
Ad-hoc-агент создан и настроен.Осталось отослать отложенный на одну секунду сигнал stop:
env.register_coop( std::move( coop ) );
so_5::send_delayed< stop >( env, stopper.direct_mbox(), std::chrono::seconds(1) );
SObjectizer Team, февраль 2015
Ad-hoc-агент создан и настроен.Осталось отослать отложенный на одну секунду сигнал stop:
env.register_coop( std::move( coop ) );
so_5::send_delayed< stop >( env, stopper.direct_mbox(), std::chrono::seconds(1) );
Функция so_5::send_delayed отсылает отложенное на указанное время сообщение или сигнал.В данном случае сигнал stop на direct_mbox нового ad-hoc-агента через одну секунду.
SObjectizer Team, февраль 2015
В итоге стартовая функция приняла вид:void init( so_5::rt::environment_t & env ){ auto coop = env.create_coop( so_5::autoname );
auto a_pinger = coop->add_agent( new pinger( env ) ); auto a_ponger = coop->add_agent( new ponger( env ) );
a_pinger->set_ponger_mbox( a_ponger->so_direct_mbox() ); a_ponger->set_pinger_mbox( a_pinger->so_direct_mbox() );
struct stop : public so_5::rt::signal_t {};
auto stopper = coop->define_agent(); stopper.event< stop >( stopper.direct_mbox(), [&env]{ env.stop(); } );
env.register_coop( std::move( coop ) );
so_5::send_delayed< stop >( env, stopper.direct_mbox(), std::chrono::seconds(1) );}
SObjectizer Team, февраль 2015
Запускаем обновленный пример...
SObjectizer Team, февраль 2015
Запускаем обновленный пример...
Получаем...
pongs: 4441168pings: 4441169
SObjectizer Team, февраль 2015
Запускаем обновленный пример...
Получаем...
pongs: 4441168pings: 4441169
Итого больше 8M сообщений в секунду.
SObjectizer Team, февраль 2015
Итого больше 8M сообщений в секунду
Core i7 2.4GHz, 8GiB RAM, Win8.1 64-bit,Visual C++ 2013 64-bit
Запускаем обновленный пример...
Получаем...
pongs: 4441168pings: 4441169
SObjectizer Team, февраль 2015
Итого больше 8M сообщений в секунду
Core i7 2.4GHz, 8GiB RAM, Win8.1 64-bit,Visual C++ 2013 64-bit
Запускаем обновленный пример...
Получаем...
pongs: 4441168pings: 4441169
Это хорошо, но на каком контексте работают агенты в данном примере?
SObjectizer Team, февраль 2015
Все агенты работают на одной общей рабочей нити!
SObjectizer Team, февраль 2015
Все агенты работают на одной общей рабочей нити!
Т.е. никакой многопоточности пока не видно. Пример показал лишь возможности по передаче сообщений между агентами, разделяющими общий рабочий контекст.
SObjectizer Team, февраль 2015
Все агенты работают на одной общей рабочей нити!
Т.е. никакой многопоточности пока не видно. Пример показал лишь возможности по передачи сообщений между агентами, разделяющими общий рабочий контекст.
Но кто выбирает рабочий контекст для агентов?И как привязать агента к другому контексту?
SObjectizer Team, февраль 2015
Все агенты работают на одной общей рабочей нити!
Т.е. никакой многопоточности пока не видно. Пример показал лишь возможности по передачи сообщений между агентами, разделяющими общий рабочий контекст.
Но кто выбирает рабочий контекст для агентов?И как привязать агента к другому контексту?
Контекст выбирает программист, указывая, на каком диспетчере должен работать агент.Если диспетчер не указан, то агент привязывается к диспетчеру по умолчанию.
SObjectizer Team, февраль 2015
Все агенты работают на одной общей рабочей нити!
Т.е. никакой многопоточности пока не видно. Пример показал лишь возможности по передачи сообщений между агентами, разделяющими общий рабочий контекст.
Но кто выбирает рабочий контекст для агентов?И как привязать агента к другому контексту?
Контекст выбирает программист, указывая, на каком диспетчере должен работать агент.Если диспетчер не указан, то агент привязывается к диспетчеру по умолчанию.
Как это и произошло в данном примере.
SObjectizer Team, февраль 2015
Диспетчер по умолчанию запускает события всех своих агентов на одной общей рабочей нити.
Для этих агентов получается что-то вроде кооперативной многозадачности. Если кто-то стал “тормозить”, то “тормозить” начинают и остальные.
SObjectizer Team, февраль 2015
Диспетчер по умолчанию запускает события всех своих агентов на одной общей рабочей нити.
Для этих агентов получается что-то вроде кооперативной многозадачности. Если кто-то стал “тормозить”, то “тормозить” начинают и остальные.
Но можно создать произвольное количество необходимых приложению диспетчеров и
привязать своих агентов к этим диспетчерам.
SObjectizer Team, февраль 2015
Заставим агентов pinger и ponger работать на разных рабочих нитях (чтобы у каждого из них была своя собственная рабочая нить)...
SObjectizer Team, февраль 2015
Заставим агентов pinger и ponger работать на разных рабочих нитях (чтобы у каждого из них была своя собственная рабочая нить)...
Для этого создадим диспетчера active_obj и привяжем агентов к нему.
SObjectizer Team, февраль 2015
Заставим агентов pinger и ponger работать на разных рабочих нитях (чтобы у каждого из них была своя собственная рабочая нить)...
Для этого создадим диспетчера active_obj и привяжем агентов к нему.
Данный диспетчер каждому своему агенту выделяет отдельную рабочую нить (агент оказывается активным объектом).
SObjectizer Team, февраль 2015
Для этого ничего не нужно менять в агентах...
SObjectizer Team, февраль 2015
Для этого ничего не нужно менять в агентах...
Изменения затронут только стартовую функцию.
SObjectizer Team, февраль 2015
Привязка агентов к разным диспетчерам:void init( so_5::rt::environment_t & env ){ env.add_dispatcher_if_not_exists( "active_obj", &so_5::disp::active_obj::create_disp );
auto coop = env.create_coop( so_5::autoname, so_5::disp::active_obj::create_disp_binder("active_obj") );
auto a_pinger = coop->add_agent( new pinger( env ) ); auto a_ponger = coop->add_agent( new ponger( env ) );
a_pinger->set_ponger_mbox( a_ponger->so_direct_mbox() ); a_ponger->set_pinger_mbox( a_pinger->so_direct_mbox() );
struct stop : public so_5::rt::signal_t {};
auto stopper = coop->define_agent( so_5::rt::create_default_disp_binder() ); stopper.event< stop >( stopper.direct_mbox(), [&env]{ env.stop(); } );
env.register_coop( std::move( coop ) );
so_5::send_delayed< stop >( env, stopper.direct_mbox(), std::chrono::seconds(1) );}
SObjectizer Team, февраль 2015
Привязка агентов к разным диспетчерам:void init( so_5::rt::environment_t & env ){ env.add_dispatcher_if_not_exists( "active_obj", &so_5::disp::active_obj::create_disp );
auto coop = env.create_coop( so_5::autoname, so_5::disp::active_obj::create_disp_binder("active_obj") );
auto a_pinger = coop->add_agent( new pinger( env ) ); auto a_ponger = coop->add_agent( new ponger( env ) );
a_pinger->set_ponger_mbox( a_ponger->so_direct_mbox() ); a_ponger->set_pinger_mbox( a_pinger->so_direct_mbox() );
struct stop : public so_5::rt::signal_t {};
auto stopper = coop->define_agent( so_5::rt::create_default_disp_binder() ); stopper.event< stop >( stopper.direct_mbox(), [&env]{ env.stop(); } );
env.register_coop( std::move( coop ) );
so_5::send_delayed< stop >( env, stopper.direct_mbox(), std::chrono::seconds(1) );}
Просьба создать диспетчера с активными объектами под именем “active_obj”. Если такого диспетчера еще нет, то он будет создан с помощью указанной фабрики.
SObjectizer Team, февраль 2015
Привязка агентов к разным диспетчерам:void init( so_5::rt::environment_t & env ){ env.add_dispatcher_if_not_exists( "active_obj", &so_5::disp::active_obj::create_disp );
auto coop = env.create_coop( so_5::autoname, so_5::disp::active_obj::create_disp_binder("active_obj") );
auto a_pinger = coop->add_agent( new pinger( env ) ); auto a_ponger = coop->add_agent( new ponger( env ) );
a_pinger->set_ponger_mbox( a_ponger->so_direct_mbox() ); a_ponger->set_pinger_mbox( a_pinger->so_direct_mbox() );
struct stop : public so_5::rt::signal_t {};
auto stopper = coop->define_agent( so_5::rt::create_default_disp_binder() ); stopper.event< stop >( stopper.direct_mbox(), [&env]{ env.stop(); } );
env.register_coop( std::move( coop ) );
so_5::send_delayed< stop >( env, stopper.direct_mbox(), std::chrono::seconds(1) );}
Указание кооперации о том, что основным диспетчером для ее агентов будет диспетчер с именем “active_obj”.
SObjectizer Team, февраль 2015
Привязка агентов к разным диспетчерам:void init( so_5::rt::environment_t & env ){ env.add_dispatcher_if_not_exists( "active_obj", &so_5::disp::active_obj::create_disp );
auto coop = env.create_coop( so_5::autoname, so_5::disp::active_obj::create_disp_binder("active_obj") );
auto a_pinger = coop->add_agent( new pinger( env ) ); auto a_ponger = coop->add_agent( new ponger( env ) );
a_pinger->set_ponger_mbox( a_ponger->so_direct_mbox() ); a_ponger->set_pinger_mbox( a_pinger->so_direct_mbox() );
struct stop : public so_5::rt::signal_t {};
auto stopper = coop->define_agent( so_5::rt::create_default_disp_binder() ); stopper.event< stop >( stopper.direct_mbox(), [&env]{ env.stop(); } );
env.register_coop( std::move( coop ) );
so_5::send_delayed< stop >( env, stopper.direct_mbox(), std::chrono::seconds(1) );}
Агенты pinger и ponger добавляются в кооперацию без каких-либо дополнительных инструкций. Значит они будут привязаны к тому диспетчеру, который для кооперации считается основным диспетчером. В данном случае это будет диспетчер с именем “active_obj”.
SObjectizer Team, февраль 2015
Привязка агентов к разным диспетчерам:void init( so_5::rt::environment_t & env ){ env.add_dispatcher_if_not_exists( "active_obj", &so_5::disp::active_obj::create_disp );
auto coop = env.create_coop( so_5::autoname, so_5::disp::active_obj::create_disp_binder("active_obj") );
auto a_pinger = coop->add_agent( new pinger( env ) ); auto a_ponger = coop->add_agent( new ponger( env ) );
a_pinger->set_ponger_mbox( a_ponger->so_direct_mbox() ); a_ponger->set_pinger_mbox( a_pinger->so_direct_mbox() );
struct stop : public so_5::rt::signal_t {};
auto stopper = coop->define_agent( so_5::rt::create_default_disp_binder() ); stopper.event< stop >( stopper.direct_mbox(), [&env]{ env.stop(); } );
env.register_coop( std::move( coop ) );
so_5::send_delayed< stop >( env, stopper.direct_mbox(), std::chrono::seconds(1) );}
А вот для агента stopper-а отдельная рабочая нить не нужна. Поэтому этот объект явным образом привязывается к диспетчеру SObjectizer по умолчанию.Если такого прямого указания не сделать, то для агента будет выполнена привязка к основному диспетчеру кооперации.
SObjectizer Team, февраль 2015
Что получается после запуска обновленного примера?
SObjectizer Team, февраль 2015
Что получается после запуска обновленного примера?
pings: pongs: 12346231234624
SObjectizer Team, февраль 2015
Что получается после запуска обновленного примера?
pings: pongs: 12346231234624
Упс… Или так и должно быть?
SObjectizer Team, февраль 2015
Что получается после запуска обновленного примера?
pings: pongs: 12346231234624
Упс… Или так и должно быть?
Это, действительно упс. Но так и должно быть
SObjectizer Team, февраль 2015
Что получается после запуска обновленного примера?
pings: pongs: 12346231234624
Упс… Или так и должно быть?
Это, действительно упс. Но так и должно быть Могло бы быть и еще страшнее
SObjectizer Team, февраль 2015
Что же произошло?
pings: pongs: 12346231234624
SObjectizer Team, февраль 2015
Что же произошло?
pings: pongs: 12346231234624
Агенты pinger и ponger стали работать на разных нитях и конкурировать за доступ к std::cout. В результате этой конкуренции вывод в std::cout перемешался. Мог бы перемешаться еще больше. А мог бы и не перемешаться вовсе. Многопоточность…
SObjectizer Team, февраль 2015
Что еще произошло?
pings: pongs: 12346231234624
SObjectizer Team, февраль 2015
Что еще произошло?
pings: pongs: 12346231234624
Упала общая производительность примера.Если на одной нити был показан результат в8M сообщений в секунду, то на двух нитях - всего 2M сообщений.
SObjectizer Team, февраль 2015
Что еще произошло?
pings: pongs: 12346231234624
Упала общая производительность примера.Если на одной нити был показан результат в8M сообщений в секунду, то на двух нитях - всего 2M сообщений.
Что вполне ожидаемо, т.к. передача единичных сообщений с одной нити на другую - это
дорогостоящая операция.
SObjectizer Team, февраль 2015
Но что изменилось в самих агентах?
SObjectizer Team, февраль 2015
Но что изменилось в самих агентах?
Ничего.
SObjectizer Team, февраль 2015
Агент ponger дляодной рабочей нити:
class ponger : public so_5::rt::agent_t{public : ponger( so_5::rt::environment_t & env ) : so_5::rt::agent_t( env ) {}
void set_pinger_mbox( const so_5::rt::mbox_t & mbox ) { m_pinger = mbox; }
virtual void so_define_agent() override { so_default_state().event< ping >( [this]{ ++m_pings; so_5::send< pong >( m_pinger ); } ); }
virtual void so_evt_finish() override { std::cout << "pings: " << m_pings << std::endl; }
private : so_5::rt::mbox_t m_pinger; unsigned int m_pings = 0;};
SObjectizer Team, февраль 2015
Агент ponger дляодной рабочей нити:
class ponger : public so_5::rt::agent_t{public : ponger( so_5::rt::environment_t & env ) : so_5::rt::agent_t( env ) {}
void set_pinger_mbox( const so_5::rt::mbox_t & mbox ) { m_pinger = mbox; }
virtual void so_define_agent() override { so_default_state().event< ping >( [this]{ ++m_pings; so_5::send< pong >( m_pinger ); } ); }
virtual void so_evt_finish() override { std::cout << "pings: " << m_pings << std::endl; }
private : so_5::rt::mbox_t m_pinger; unsigned int m_pings = 0;};
Агент ponger длядвух рабочих нитей:
class ponger : public so_5::rt::agent_t{public : ponger( so_5::rt::environment_t & env ) : so_5::rt::agent_t( env ) {}
void set_pinger_mbox( const so_5::rt::mbox_t & mbox ) { m_pinger = mbox; }
virtual void so_define_agent() override { so_default_state().event< ping >( [this]{ ++m_pings; so_5::send< pong >( m_pinger ); } ); }
virtual void so_evt_finish() override { std::cout << "pings: " << m_pings << std::endl; }
private : so_5::rt::mbox_t m_pinger; unsigned int m_pings = 0;};
SObjectizer Team, февраль 2015
Это прямое следствие того, что агенты взаимодействовали друг с другом только посредством асинхронных сообщений.
SObjectizer Team, февраль 2015
Это прямое следствие того, что агенты взаимодействовали друг с другом только посредством асинхронных сообщений.
Поэтому им все равно, на каком контексте они работают.
SObjectizer Team, февраль 2015
Это прямое следствие того, что агенты взаимодействовали друг с другом только посредством асинхронных сообщений.
Поэтому им все равно, на каком контексте они работают.
А задачей SObjectizer-а является предоставление программисту возможности
выбрать нужный ему контекст путем привязки агентов к соответствующим
диспетчерам.
SObjectizer Team, февраль 2015
Для этого в SObjectizer есть целый ряд готовых диспетчеров “из коробки”:
SObjectizer Team, февраль 2015
Для этого в SObjectizer есть целый ряд готовых диспетчеров “из коробки”:
● one_thread. Запускает всех агентов на одной общей рабочей нити;
SObjectizer Team, февраль 2015
Для этого в SObjectizer есть целый ряд готовых диспетчеров “из коробки”:
● one_thread. Запускает всех агентов на одной общей рабочей нити;● active_obj. Предоставляет каждому агенту отдельную нить в
единоличное пользование;
SObjectizer Team, февраль 2015
Для этого в SObjectizer есть целый ряд готовых диспетчеров “из коробки”:
● one_thread. Запускает всех агентов на одной общей рабочей нити;● active_obj. Предоставляет каждому агенту отдельную нить в
единоличное пользование;● active_group. Предоставляет отдельную нить в единоличное
пользование группе объектов;
SObjectizer Team, февраль 2015
Для этого в SObjectizer есть целый ряд готовых диспетчеров “из коробки”:
● one_thread. Запускает всех агентов на одной общей рабочей нити;● active_obj. Предоставляет каждому агенту отдельную нить в
единоличное пользование;● active_group. Предоставляет отдельную нить в единоличное
пользование группе объектов;● thread_pool. Выделяет агентам нити из пула рабочих нитей. Агенты
могут мигрировать с одной рабочей нити на другую. Но агент не может работать на двух рабочих нитях одновременно;
SObjectizer Team, февраль 2015
Для этого в SObjectizer есть целый ряд готовых диспетчеров “из коробки”:
● one_thread. Запускает всех агентов на одной общей рабочей нити;● active_obj. Предоставляет каждому агенту отдельную нить в
единоличное пользование;● active_group. Предоставляет отдельную нить в единоличное
пользование группе объектов;● thread_pool. Выделяет агентам нити из пула рабочих нитей. Агенты
могут мигрировать с одной рабочей нити на другую. Но агент не может работать на двух рабочих нитях одновременно;
● adv_thread_pool. Выделяет агентам нити из пула рабочих нитей. Агенты могут и мигрировать с одной рабочей нити на другую, и работать сразу на нескольких (при условии, что их обработчики событий объявлены thread safe).
SObjectizer Team, февраль 2015
При этом разработчик может не только выбирать нужный ему тип диспетчера...
SObjectizer Team, февраль 2015
При этом разработчик может не только выбирать нужный ему тип диспетчера...
...Но и создавать в своем приложении нужное ему количество нужных ему типов диспетчеров.
SObjectizer Team, февраль 2015
При этом разработчик может не только выбирать нужный ему тип диспетчера...
...Но и создавать в своем приложении нужное ему количество нужных ему типов диспетчеров.
Например:
SObjectizer Team, февраль 2015
При этом разработчик может не только выбирать нужный ему тип диспетчера...
...Но и создавать в своем приложении нужное ему количество нужных ему типов диспетчеров.
Например:● один one_thread диспетчер для агента-клиента AMQP;
SObjectizer Team, февраль 2015
При этом разработчик может не только выбирать нужный ему тип диспетчера...
...Но и создавать в своем приложении нужное ему количество нужных ему типов диспетчеров.
Например:● один one_thread диспетчер для агента-клиента AMQP;● один thread_pool диспетчер для обработки прочитанных из AMQP-
очередей запросов;
SObjectizer Team, февраль 2015
При этом разработчик может не только выбирать нужный ему тип диспетчера...
...Но и создавать в своем приложении нужное ему количество нужных ему типов диспетчеров.
Например:● один one_thread диспетчер для агента-клиента AMQP;● один thread_pool диспетчер для обработки прочитанных из AMQP-
очередей запросов;● один active_obj диспетчер для агентов, выполняющих работу с СУБД;
SObjectizer Team, февраль 2015
При этом разработчик может не только выбирать нужный ему тип диспетчера...
...Но и создавать в своем приложении нужное ему количество нужных ему типов диспетчеров.
Например:● один one_thread диспетчер для агента-клиента AMQP;● один thread_pool диспетчер для обработки прочитанных из AMQP-
очередей запросов;● один active_obj диспетчер для агентов, выполняющих работу с СУБД;● еще один active_obj диспетчер для агентов, работающих с
подключенными к компьютеру HSM-ами;
SObjectizer Team, февраль 2015
При этом разработчик может не только выбирать нужный ему тип диспетчера...
...Но и создавать в своем приложении нужное ему количество нужных ему типов диспетчеров.
Например:● один one_thread диспетчер для агента-клиента AMQP;● один thread_pool диспетчер для обработки прочитанных из AMQP-
очередей запросов;● один active_obj диспетчер для агентов, выполняющих работу с СУБД;● еще один active_obj диспетчер для агентов, работающих с
подключенными к компьютеру HSM-ами;● и еще один thread_pool диспетчер для агентов, следящих за всей этой
анархией
SObjectizer Team, февраль 2015
Но и это еще не все, что есть в SObjectizer...
SObjectizer Team, февраль 2015
Но и это еще не все, что есть в SObjectizer...
Такие важные вещи, как:
SObjectizer Team, февраль 2015
Но и это еще не все, что есть в SObjectizer...
Такие важные вещи, как:● состояния агентов;
SObjectizer Team, февраль 2015
Но и это еще не все, что есть в SObjectizer...
Такие важные вещи, как:● состояния агентов;● периодические сообщения;
SObjectizer Team, февраль 2015
Но и это еще не все, что есть в SObjectizer...
Такие важные вещи, как:● состояния агентов;● периодические сообщения;● синхронное взаимодействие агентов;
SObjectizer Team, февраль 2015
Но и это еще не все, что есть в SObjectizer...
Такие важные вещи, как:● состояния агентов;● периодические сообщения;● синхронное взаимодействие агентов;● дочерние кооперации;
SObjectizer Team, февраль 2015
Но и это еще не все, что есть в SObjectizer...
Такие важные вещи, как:● состояния агентов;● периодические сообщения;● синхронное взаимодействие агентов;● дочерние кооперации;● обработка выпущенных агентами наружу
исключений;
SObjectizer Team, февраль 2015
Но и это еще не все, что есть в SObjectizer...
Такие важные вещи, как:● состояния агентов;● периодические сообщения;● синхронное взаимодействие агентов;● дочерние кооперации;● обработка выпущенных агентами наружу
исключений;● тонкая настройка параметров Run-Time и пр...
SObjectizer Team, февраль 2015
Но и это еще не все, что есть в SObjectizer...
Такие важные вещи, как:● состояния агентов;● периодические сообщения;● синхронное взаимодействие агентов;● дочерние кооперации;● обработка выпущенных агентами наружу исключений;● тонкая настройка параметров Run-Time и пр...
Будут рассмотрены при дальнейшем погружении
SObjectizer Team, февраль 2015
Но и это еще не все, что есть в SObjectizer...
Такие важные вещи, как:● состояния агентов;● периодические сообщения;● синхронное взаимодействие агентов;● дочерние кооперации;● обработка выпущенных агентами наружу исключений;● тонкая настройка параметров Run-Time и пр...
Будут рассмотрены при дальнейшем погружении Вводная же часть закончена.
Дополнительная информация:
Сайт проекта: http://sourceforge.net/projects/sobjectizer
Документация: http://sourceforge.net/p/sobjectizer/wiki/
Форум: http://sourceforge.net/p/sobjectizer/discussion/
Google-группа: https://groups.google.com/forum/#!forum/sobjectizer