SlideShare une entreprise Scribd logo
1  sur  70
Télécharger pour lire hors ligne
C++ Russia 2017
Для чего мы делали свой
акторный фреймворк и что из
этого вышло?
Евгений Охотников
А делали ли мы акторный фреймворк вообще?
?
2
Давным-давно, в далекой-далекой...
1994-й год.
Гомель.
КБ Системного Программирования.
Отдел АСУТП.
Попытка сделать объектно-ориентированную SCADA-систему.
SCADA - Supervisory Control And Data Acquisition
3
О SCADA-системах в двух словах
4
http://www.scadasoftware.net/
Объектная SCADA. Зачем?
Традиционный подход SCADA-систем в те времена:
● набор нумерованных или именованных каналов ввода-вывода (тегов);
● слабая структуризация и почти отсутствие декомпозиции;
● трудозатраты растут с увеличением числа тегов;
● трудозатраты еще быстрее растут по мере усложнения логики
автоматизируемого техпроцесса.
Мы хотели устранить это за счет декомпозиции на объекты,
обладающие состоянием и поведением.
5
Объектная SCADA. Что получилось?
Агенты:
● конечные автоматы с явно выделенными состояниями;
● состояния агентов видны снаружи.
6
Объектная SCADA. Что получилось?
Взаимодействие через асинхронные сообщения:
● прицел на soft real-time;
● распространение информации как в режиме 1:1, так и в
режиме 1:N.
7
Объектная SCADA. Что получилось?
Диспетчер.
Отвечал за обработку очередей сообщений с учетом приоритетов
и требований soft real-time.
8
Объектная SCADA. Итог
SCADA Objectizer.
1998-й год.
Опробован в реальном проекте.
Прекратил свое существование в начале 2000-го :(
9
Предпосылки к перерождению
Начало 2002-го.
Компания "Интервэйл".
Двое участников разработки SCADA Objectizer.
Небольшая GUI-программа для управления подключенными к ПК
устройствами...
10
Четвертое пришествие
Апрель 2002-го года.
SObjectizer = SCADA + Objectizer.
SObjectizer-4 (в четвертый раз все сначала).
11
Этапы большого пути
Май 2002-го: начало использования SO-4 в разработке.
Март 2006-го: перевод SO-4 в категорию OpenSource проектов
под BSD-лицензией.
Сентябрь 2010-го: начало работ над SObjectizer-5.
Май 2013-го: публичный релиз SO-5 под BSD-лицензией.
Июль 2013-го: SObjectizer начал жить независимо от "Интервэйл".
12
На данный момент...
Последняя стабильная версия SObjectizer-5.5.18 – это:
● 25KLOC кода (+28KLOC кода в тестах +10KLOC кода в
примерах);
● работа на платформах Windows, Linux, FreeBSD, MacOS, Android
(через CrystaX NDK);
● поддержка компиляторов VC++ 12.0-14.0, GCC 4.8-6.3, clang
3.5-3.9;
● документация, презентации, статьи;
● отсутствие больших ломающих изменений с октября 2014-го.
13
Тем не менее...
Для чего же мы делали свой
фреймворк?
14
Как перестать бояться и...
...полюбить многопоточность?
Голые thread, mutex и condition_variable – это пот, кровь и боль.
Асинхронный обмен сообщениями рулит!
И бибикает :)
Ибо shared nothing и вот этот вот все.
15
Бонусы асинхронного обмена сообщениями
● у каждого агента свое изолированное состояние (принцип
shared nothing), упрощает жизнь в многопоточном коде;
● обмен сообщениями – естественный подход к решению
некоторых типов задач;
● слабая связность между компонентами;
● очень простая работа с таймерами (отложенные и
периодические сообщения);
● низкий порог входа для новичков.
16
Давайте посмотрим на
SObjectizer-5 с более близкого
расстояния
17
В SObjectizer есть сообщения
Все взаимодействие между агентами в SObjectizer идет только
через асинхронные сообщения.
Сообщения это объекты. Тип сообщения наследуется от
so_5::message_t.
18
Выглядят сообщения вот так:
struct request : public so_5::message_t
{
std::int64_t id_;
std::map<std::string, std::string> params_;
std::vector<std::uint8_t> payload_;
std::chrono::steady_clock::timepoint deadline_;
request(
std::int64_t id,
std::map<std::string, std::string> params,
std::vector<std::uint8_t> payload,
std::chrono::steady_clock::timeout deadline)
: id_(id), params_(std::move(params)), payload_(std::move(payload)), deadline_(deadline)
{}
};
struct get_status : public so_5::signal_t {};
19
Отсылаются сообщения вот так:
// Безотлагательная отсылка сообщения.
so_5::send<request>(target,
// Все это через perfect-forwarding идет в конструктор request-а.
make_id(), make_params(), make_payload(), calculate_deadline());
// Безотлагательная отсылка сигнала.
so_5::send<get_status>(target);
// Отсылка сигнала с задержкой на 250ms.
so_5::send_delayed<get_status>(target, std::chrono::milliseconds(250));
// Периодическая отсылка сигнала со стартовой задержкой в 250ms
// и периодом повтора в 750ms.
auto timer = so_5::send_periodic<get_status>(target,
std::chrono::milliseconds(250),
std::chrono::milliseconds(750));
20
Что такое Target для send?
В "традиционной" Модели Акторов адресатом сообщения
является актор.
В SObjectizer сообщения отсылаются в "почтовый ящик".
Почтовый ящик в SObjectizer называется mbox.
21
Подписка на сообщение из mbox-а
Для получения сообщения из mbox-а нужно выполнить подписку.
Только после этого сообщения будут доставляться агенту.
Подписку можно отменить. Сообщения доставляться перестанут.
Подписки агента автоматически удаляются, когда агент
уничтожается.
Сообщения диспетчируются по типу, а не по содержимому.
22
Подписка на сообщение из mbox-а
23
Multi-Producer/Single-Consumer Mbox
Кто угодно может оправить. Подписаться может только один
агент.
MPSC-mbox создается для каждого агента автоматически.
Использование MPSC-mbox-ов дает максимально близкое
приближение к Модели Акторов.
24
Multi-Producer/Multi-Consumer Mbox
Кто угодно может оправить. Получат все подписчики.
MPMC-mbox нужно создавать вручную (можно с именем, можно
без).
Простейший вариант модели Publish/Subscribe.
25
В SObjectizer есть диспетчеры
Именно диспетчер определяет где и когда агент будет
обрабатывать свои сообщения.
Каждый агент в SObjectizer должен быть привязан к конкретному
диспетчеру.
В приложении может быть запущено сразу несколько
диспетчеров.
26
Примеры диспетчеров
one_thread: все агенты на одной и той же нити, все агенты используют
одну очередь сообщений.
active_obj: у каждого агента своя собственная нить, у каждого агента
собственная очередь сообщений.
adv_thread_pool: агенты на группе нитей, агент может мигрировать с
одной нити на другую, события одного агента могут быть запущены
параллельно, если они отмечены как thread_safe. Очереди сообщений
могут быть персональными или общими для нескольких агентов.
27
Больше диспетчеров, хороших и разных
Всего в SObjectizer-5 "из коробки" доступно восемь типов
диспетчеров:
active_group
active_obj
adv_thread_pool
one_thread
prio_dedicated_threads::one_per_prio
prio_one_thread::quoted_round_robin
prio_one_thread::strictly_ordered
thread_pool
28
Зачем так много?
29
В SObjectizer есть агенты
Как правило, реализуются как объекты классов, унаследованных
от so_5::agent_t.
class hello_world final : public so_5::agent_t {
public :
using so_5::agent_t::agent_t;
virtual void so_evt_start() override {
std::cout << "Hello, World!" << std::endl;
so_deregister_agent_coop_normally();
}
};
30
Агенты – это конечные автоматы
1. Мы не поддерживаем агентов в виде сопрограмм. Поэтому
должен использоваться механизм callback-ов, чтобы
отобразить N агентов на M рабочих нитей.
2. Ноги у SObjectizer растут из мира АСУТП, там конечные
автоматы – это обычное дело.
3. Когда есть ярко выраженные состояния и разное поведение в
каждом из состояний конечный автомат удобнее, чем вызовы
receive с последующим выбором обработчика.
31
Агент blinking_led (диаграмма состояний)
32
Агент blinking_led (код)
class blinking_led final : public so_5::agent_t {
state_t off{ this }, blinking{ this },
blink_on{ initial_substate_of{ blinking } },
blink_off{ substate_of{ blinking } };
public :
struct turn_on_off : public so_5::signal_t {};
blinking_led( context_t ctx ) : so_5::agent_t{ std::move(ctx) } {
this >>= off;
off.just_switch_to< turn_on_off >( blinking );
blinking.just_switch_to< turn_on_off >( off );
blink_on
.on_enter( []{ std::cout << "ON" << std::endl; } )
.on_exit( []{ std::cout << "off" << std::endl; } )
.time_limit( std::chrono::milliseconds{1500}, blink_off );
blink_off
.time_limit( std::chrono::milliseconds{750}, blink_on );
}
};
33
Агент blinking_led (пояснения)
class blinking_led final : public so_5::agent_t {
state_t off{ this }, blinking{ this },
blink_on{ initial_substate_of{ blinking } },
blink_off{ substate_of{ blinking } };
public :
struct turn_on_off : public so_5::signal_t {};
blinking_led( context_t ctx ) : so_5::agent_t{ std::move(ctx) } {
this >>= off;
off.just_switch_to< turn_on_off >( blinking );
blinking.just_switch_to< turn_on_off >( off );
blink_on
.on_enter( []{ std::cout << "ON" << std::endl; } )
.on_exit( []{ std::cout << "off" << std::endl; } )
.time_limit( std::chrono::milliseconds{1500}, blink_off );
blink_off
.time_limit( std::chrono::milliseconds{750}, blink_on );
}
};
34
Определение двух
верхнеуровневых
состояний.
Агент blinking_led (пояснения)
class blinking_led final : public so_5::agent_t {
state_t off{ this }, blinking{ this },
blink_on{ initial_substate_of{ blinking } },
blink_off{ substate_of{ blinking } };
public :
struct turn_on_off : public so_5::signal_t {};
blinking_led( context_t ctx ) : so_5::agent_t{ std::move(ctx) } {
this >>= off;
off.just_switch_to< turn_on_off >( blinking );
blinking.just_switch_to< turn_on_off >( off );
blink_on
.on_enter( []{ std::cout << "ON" << std::endl; } )
.on_exit( []{ std::cout << "off" << std::endl; } )
.time_limit( std::chrono::milliseconds{1500}, blink_off );
blink_off
.time_limit( std::chrono::milliseconds{750}, blink_on );
}
};
35
Определение двух вложенных
состояний для состояния blinking.
Подсостояние blink_on является
начальным подсостоянием.
Агент blinking_led (пояснения)
class blinking_led final : public so_5::agent_t {
state_t off{ this }, blinking{ this },
blink_on{ initial_substate_of{ blinking } },
blink_off{ substate_of{ blinking } };
public :
struct turn_on_off : public so_5::signal_t {};
blinking_led( context_t ctx ) : so_5::agent_t{ std::move(ctx) } {
this >>= off;
off.just_switch_to< turn_on_off >( blinking );
blinking.just_switch_to< turn_on_off >( off );
blink_on
.on_enter( []{ std::cout << "ON" << std::endl; } )
.on_exit( []{ std::cout << "off" << std::endl; } )
.time_limit( std::chrono::milliseconds{1500}, blink_off );
blink_off
.time_limit( std::chrono::milliseconds{750}, blink_on );
}
};
36
Перевод агента в то состояние, в
котором он должен начать свою работу.
Кому не нравится перегрузка >>= для смены состояния
агента, для тех есть альтернативный синтаксис:
off.activate();
so_change_state(off);
Агент blinking_led (пояснения)
class blinking_led final : public so_5::agent_t {
state_t off{ this }, blinking{ this },
blink_on{ initial_substate_of{ blinking } },
blink_off{ substate_of{ blinking } };
public :
struct turn_on_off : public so_5::signal_t {};
blinking_led( context_t ctx ) : so_5::agent_t{ std::move(ctx) } {
this >>= off;
off.just_switch_to< turn_on_off >( blinking );
blinking.just_switch_to< turn_on_off >( off );
blink_on
.on_enter( []{ std::cout << "ON" << std::endl; } )
.on_exit( []{ std::cout << "off" << std::endl; } )
.time_limit( std::chrono::milliseconds{1500}, blink_off );
blink_off
.time_limit( std::chrono::milliseconds{750}, blink_on );
}
};
37
Реакция на сигнал включения и
выключения для верхних состояний.
Достаточно просто перейти в другое
состояние.
Агент blinking_led (пояснения)
class blinking_led final : public so_5::agent_t {
state_t off{ this }, blinking{ this },
blink_on{ initial_substate_of{ blinking } },
blink_off{ substate_of{ blinking } };
public :
struct turn_on_off : public so_5::signal_t {};
blinking_led( context_t ctx ) : so_5::agent_t{ std::move(ctx) } {
this >>= off;
off.just_switch_to< turn_on_off >( blinking );
blinking.just_switch_to< turn_on_off >( off );
blink_on
.on_enter( []{ std::cout << "ON" << std::endl; } )
.on_exit( []{ std::cout << "off" << std::endl; } )
.time_limit( std::chrono::milliseconds{1500}, blink_off );
blink_off
.time_limit( std::chrono::milliseconds{750}, blink_on );
}
};
38
Реакция на вход и
выход из состояния.
Агент blinking_led (пояснения)
class blinking_led final : public so_5::agent_t {
state_t off{ this }, blinking{ this },
blink_on{ initial_substate_of{ blinking } },
blink_off{ substate_of{ blinking } };
public :
struct turn_on_off : public so_5::signal_t {};
blinking_led( context_t ctx ) : so_5::agent_t{ std::move(ctx) } {
this >>= off;
off.just_switch_to< turn_on_off >( blinking );
blinking.just_switch_to< turn_on_off >( off );
blink_on
.on_enter( []{ std::cout << "ON" << std::endl; } )
.on_exit( []{ std::cout << "off" << std::endl; } )
.time_limit( std::chrono::milliseconds{1500}, blink_off );
blink_off
.time_limit( std::chrono::milliseconds{750}, blink_on );
}
};
39
time_limit задает ограничение на
время пребывания агента в
состоянии.
По истечении этого времени агент
автоматически меняет состояние.
Еще один пример: request_processor (код)
class request_processor : public so_5::agent_t {
state_t st_waiting{ this }, st_working{ this }, ...;
const so_5::mbox_t src_;
public :
request_processor(context_t ctx, so_5::mbox_t src) : so_5::agent_t(std::move(ctx)), src_(std::move(src)) {}
virtual void so_define_agent() override {
this >>= st_waiting;
st_waiting
.event(src_, &request_processor::on_request)
.event(src_, &request_processor::on_status_when_waiting);
st_working
.event(src_, &request_processor::on_request)
.event(src_, &request_processor::on_status_when_working);
...
}
...
private :
void on_request(const request & cmd) {...}
void on_status_when_waiting(mhood_t<get_status>) {...}
void on_status_when_working(mhood_t<get_status>) {...}
};
40
Еще один пример: request_processor (пояснения)
class request_processor : public so_5::agent_t {
state_t st_waiting{ this }, st_working{ this }, ...;
const so_5::mbox_t src_;
public :
request_processor(context_t ctx, so_5::mbox_t src) : so_5::agent_t(std::move(ctx)), src_(std::move(src)) {}
virtual void so_define_agent() override {
this >>= st_waiting;
st_waiting
.event(src_, &request_processor::on_request)
.event(src_, &request_processor::on_status_when_waiting);
st_working
.event(src_, &request_processor::on_request)
.event(src_, &request_processor::on_status_when_working);
...
}
...
private :
void on_request(const request & cmd) {...}
void on_status_when_waiting(mhood_t<get_status>) {...}
void on_status_when_working(mhood_t<get_status>) {...}
};
41
Почтовый ящик, из которого агент ожидает
запросы. Создается кем-то и отдается
агенту в качестве параметра конструктора.
Еще один пример: request_processor (пояснения)
class request_processor : public so_5::agent_t {
state_t st_waiting{ this }, st_working{ this }, ...;
const so_5::mbox_t src_;
public :
request_processor(context_t ctx, so_5::mbox_t src) : so_5::agent_t(std::move(ctx)), src_(std::move(src)) {}
virtual void so_define_agent() override {
this >>= st_waiting;
st_waiting
.event(src_, &request_processor::on_request)
.event(src_, &request_processor::on_status_when_waiting);
st_working
.event(src_, &request_processor::on_request)
.event(src_, &request_processor::on_status_when_working);
...
}
...
private :
void on_request(const request & cmd) {...}
void on_status_when_waiting(mhood_t<get_status>) {...}
void on_status_when_working(mhood_t<get_status>) {...}
};
42
Метод so_define_agent() дает
возможность агенту произвести
"настройку" перед тем, как начать
работать внутри SObjectizer.
Обычно используется для создания
подписок.
Еще один пример: request_processor (пояснения)
class request_processor : public so_5::agent_t {
state_t st_waiting{ this }, st_working{ this }, ...;
const so_5::mbox_t src_;
public :
request_processor(context_t ctx, so_5::mbox_t src) : so_5::agent_t(std::move(ctx)), src_(std::move(src)) {}
virtual void so_define_agent() override {
this >>= st_waiting;
st_waiting
.event(src_, &request_processor::on_request)
.event(src_, &request_processor::on_status_when_waiting);
st_working
.event(src_, &request_processor::on_request)
.event(src_, &request_processor::on_status_when_working);
...
}
...
private :
void on_request(const request & cmd) {...}
void on_status_when_waiting(mhood_t<get_status>) {...}
void on_status_when_working(mhood_t<get_status>) {...}
};
43
Подписка на сообщения.
Явным образом задается mbox из которого ожидаются
сообщения.
Тип сообщения не указан явно, он выводится
автоматически из типа аргумента обработчика.
Еще один пример: request_processor (пояснения)
class request_processor : public so_5::agent_t {
state_t st_waiting{ this }, st_working{ this }, ...;
const so_5::mbox_t src_;
public :
request_processor(context_t ctx, so_5::mbox_t src) : so_5::agent_t(std::move(ctx)), src_(std::move(src)) {}
virtual void so_define_agent() override {
this >>= st_waiting;
st_waiting
.event(src_, &request_processor::on_request)
.event(src_, &request_processor::on_status_when_waiting);
st_working
.event(src_, &request_processor::on_request)
.event(src_, &request_processor::on_status_when_working);
...
}
...
private :
void on_request(const request & cmd) {...}
void on_status_when_waiting(mhood_t<get_status>) {...}
void on_status_when_working(mhood_t<get_status>) {...}
};
44
Обработчик для сообщений с типом request.
Такой формат обработчика требует, чтобы
request был сообщением, а не сигналом.
Еще один пример: request_processor (пояснения)
class request_processor : public so_5::agent_t {
state_t st_waiting{ this }, st_working{ this }, ...;
const so_5::mbox_t src_;
public :
request_processor(context_t ctx, so_5::mbox_t src) : so_5::agent_t(std::move(ctx)), src_(std::move(src)) {}
virtual void so_define_agent() override {
this >>= st_waiting;
st_waiting
.event(src_, &request_processor::on_request)
.event(src_, &request_processor::on_status_when_waiting);
st_working
.event(src_, &request_processor::on_request)
.event(src_, &request_processor::on_status_when_working);
...
}
...
private :
void on_request(const request & cmd) {...}
void on_status_when_waiting(mhood_t<get_status>) {...}
void on_status_when_working(mhood_t<get_status>) {...}
};
45
Обработчики для сообщений или сигналов
с типом get_status.
Такой формат обработчика позволяет
обрабатывать и сообщения, и сигналы. Что
удобно при написании обобщенного кода в
агентах.
Кооперации агентов
Кооперации агентов решают задачу одномоментной регистрации
в SObjectizer группы взаимосвязанных агентов.
46
Если бы были супервизоры, то...
47
Супервизоров нет, есть кооперации
48
Наполнение и регистрация коопераций
so_5::environment_t & env = ...; // Доступ к SObjectizer.
// Заставляем SObjectizer создать объект кооперации.
auto coop = env.create_coop( "data_acquire_demo" );
// Наполняем кооперацию прикладными агентами. Заодно привязываем агентов к разным диспетчерам для того,
// чтобы агенты работали на разных рабочих нитях.
coop->make_agent_with_binder<device_reader>(
so_5::disp::one_thread::create_private_disp(env, "device")->binder(),
... );
coop->make_agent<data_processor>(...);
coop->make_agent_with_binder<db_writer>(
so_5::disp::one_thread::create_private_disp(env, "db")->binder(), ... );
// Осталось только зарегистрировать кооперацию.
env.register_coop(std::move(coop));
49
(1)
Наполнение и регистрация коопераций
so_5::environment_t & env = ...; // Доступ к SObjectizer.
// Заставляем SObjectizer создать объект кооперации.
auto coop = env.create_coop( "data_acquire_demo" );
// Наполняем кооперацию прикладными агентами. Заодно привязываем агентов к разным диспетчерам для того,
// чтобы агенты работали на разных рабочих нитях.
coop->make_agent_with_binder<device_reader>(
so_5::disp::one_thread::create_private_disp(env, "device")->binder(),
... );
coop->make_agent<data_processor>(...);
coop->make_agent_with_binder<db_writer>(
so_5::disp::one_thread::create_private_disp(env, "db")->binder(), ... );
// Осталось только зарегистрировать кооперацию.
env.register_coop(std::move(coop));
50
(2)
Наполнение и регистрация коопераций
so_5::environment_t & env = ...; // Доступ к SObjectizer.
// Заставляем SObjectizer создать объект кооперации.
auto coop = env.create_coop( "data_acquire_demo" );
// Наполняем кооперацию прикладными агентами. Заодно привязываем агентов к разным диспетчерам для того,
// чтобы агенты работали на разных рабочих нитях.
coop->make_agent_with_binder<device_reader>(
so_5::disp::one_thread::create_private_disp(env, "device")->binder(),
... );
coop->make_agent<data_processor>(...);
coop->make_agent_with_binder<db_writer>(
so_5::disp::one_thread::create_private_disp(env, "db")->binder(), ... );
// Осталось только зарегистрировать кооперацию.
env.register_coop(std::move(coop));
51
(3)
Регистрация кооперации – это просто?
1. Проверка уникальности имени.
2. Проверка родительской кооперации (если есть).
3. Запрос ресурсов у диспетчеров.
4. Вызов so_define_agent() для каждого агента.
5. Окончательная привязка агентов к диспетчерам, инициация
so_evt_start().
52
По традиции: hello_world
#include <so_5/all.hpp>
class hello_world final : public so_5::agent_t {
public :
using so_5::agent_t::agent_t;
virtual void so_evt_start() override {
std::cout << "Hello, World!" << std::endl;
so_deregister_agent_coop_normally();
}
};
int main() {
so_5::launch([](so_5::environment_t & env) {
env.register_agent_as_coop("hello", env.make_agent<hello_world>());
});
return 0;
}
53
Распределенности в SObjectizer-5 нет
Распределенность "из коробки" была в SObjectizer-4. Но:
● для разных задач нужны разные протоколы (одно дело –
передача телеметрии, другое – передача больших BLOB-ов);
● back pressure в асинхронном обмене сообщении сам по себе
непрост. В случае IPC эта проблема усугубляется;
● интероперабельность с другими языками программирования.
Точнее, ее отсутствие.
Поэтому в SObjectizer-5 распределенности нет.
54
Куда же мы идем?
55
Реализация Модели Акторов не самоцель
У нас нет цели сделать из SObjectizer самую лучшую и/или самую
полноценную реализацию Модели Акторов.
Мы говорим "акторный фреймворк" только потому, что:
● так проще объяснять, что это в принципе такое;
● маркетинг.
56
Publish/Subscribe
Поддержка Publish/Subscribe была с самого начала.
Более того, в SObjectizer-5 понятие direct mbox-а появилось не
сразу, изначально были только MPMC-mbox-ы.
57
CSP (Communicating Sequential Processes)
В декабре 2015-го добавлены message chains.
struct ping {}; struct pong {};
auto ch1 = so_5::create_mchain(env);
auto ch2 = so_5::create_mchain(env);
std::thread pinger{ [ch1, ch2]{
while(true) {
so_5::send<ping>(ch2);
so_5::receive(ch1, so_5::infinite_wait, [](pong){});
}
} };
std::thread ponger{ [ch1, ch2]{
while(true) {
so_5::receive(ch2, so_5::infinite_wait, [&ch1](ping) { so_5::send<pong>(ch1); } );
}
} };
58
CSP (Communicating Sequential Processes)
В декабре 2015-го добавлены message chains.
struct ping {}; struct pong {};
auto ch1 = so_5::create_mchain(env);
auto ch2 = so_5::create_mchain(env);
std::thread pinger{ [ch1, ch2]{
while(true) {
so_5::send<ping>(ch2);
so_5::receive(ch1, so_5::infinite_wait, [](pong){});
}
} };
std::thread ponger{ [ch1, ch2]{
while(true) {
so_5::receive(ch2, so_5::infinite_wait, [&ch1](ping) { so_5::send<pong>(ch1); } );
}
} };
59
Поэтому на самом-то деле...
It's all about in-process message
dispatching!
60
Наша же цель в том, чтобы...
...предоставить разработчику небольшой качественный и
стабильный инструмент.
Практичный и настраиваемый под нужды пользователя.
Стабильность и совместимость. За это мы готовы платить.
Например, поддержка Ubuntu 14.04 LTS и тамошнего gcc-4.8 для
нас важнее, чем возможность использовать C++14 в коде
SObjectizer.
61
Кстати!
О современном C++
62
В двух словах
Современный C++ для нас очень важен.
Даже так: если бы не C++11, SObjectizer-5 вряд ли появился бы.
Есть в C++11 вещи, без которых SObjectizer сейчас сложно
представить...
63
Variadic templates и perfect forwarding
Используются в SObjectizer повсеместно:
template<typename MSG, typename... ARGS>
void send(const mbox_t & to, ARGS &&... args)
{
auto m = make_unique<MSG>(std::forward<ARGS>(args)...);
to->deliver_message(std::move(m));
}
class agent_coop_t {
public :
...
template< class AGENT, typename... ARGS >
AGENT * make_agent( ARGS &&... args ) {
auto a = make_unique< AGENT >( environment(), std::forward<ARGS>(args)... );
return this->add_agent( std::move( a ) );
}
};
64
Лямбды (особенно в сочетании с шаблонами)
Ну очень сильно помогают. В том числе и для обеспечения
гарантий безопасности исключений...
void agent_core_t::next_coop_reg_step__update_registered_coop_map(
const coop_ref_t & coop_ref,
coop_t * parent_coop_ptr )
{
m_registered_coop[ coop_ref->query_coop_name() ] = coop_ref;
m_total_agent_count += coop_ref->query_agent_count();
so_5::details::do_with_rollback_on_exception(
[&] {
next_coop_reg_step__parent_child_relation( coop_ref, parent_coop_ptr );
},
[&] {
m_total_agent_count -= coop_ref->query_agent_count();
m_registered_coop.erase( coop_ref->query_coop_name() );
} );
}
65
(1)
(2)
(3)
auto и decltype
Сложно переоценить важность auto в современном C++.
Особенно для вывода типа результата функции.
template< typename MAIN_ACTION, typename ROLLBACK_ACTION >
auto do_with_rollback_on_exception(
MAIN_ACTION main_action,
ROLLBACK_ACTION rollback_action ) -> decltype(main_action())
{
using result_type = decltype(main_action());
using namespace rollback_on_exception_details;
rollbacker_t< ROLLBACK_ACTION > rollbacker{ rollback_action };
return executor< result_type, MAIN_ACTION, ROLLBACK_ACTION >::exec(
main_action, rollbacker );
}
66
Стандартная библиотека C++11
Появление thread, mutex, condition_variable, atomic, unordered_map
и пр. в стандартной библиотеке C++11 позволило нам избавиться
от такой зависимости, как ACE.
Стало гораздо легче.
Правда, пришлось делать свою реализацию таймеров, но это уже
совсем другая история...
67
Краткое резюме по современному C++
C++11/14 – это уже совсем другой C++.
Использовать современный C++ намного удобнее, особенно, если
есть возможность пользоваться C++14.
Инфраструктура вокруг языка продолжает желать много лучшего.
Но мы над этим работаем ;)
68
Спасибо за терпение!
Вопросы?
69
SObjectizer: https://sourceforge.net/p/sobjectizer или https://github.com/eao197/so-5-5
Документация по SObjectizer: https://sourceforge.net/p/sobjectizer/wiki/Home/
Серия статей о SObjectizer на русском:
SObjectizer: что это, для чего это и почему это выглядит именно так? От простого к сложному:
Часть I, Часть II, Часть III. Акторы в виде конечных автоматов – это плохо или хорошо?
Проблема перегрузки агентов и средства борьбы с ней. Нежная дружба агентов и
исключений.
Серия презентаций о SObjectizer на английском "Dive into SObjectizer-5.5":
Intro, Agent's States, More About Coops, Exceptions, Timers, Synchonous Interaction, Message
Limits, Dispatchers, Message Chains.
70

Contenu connexe

Tendances

Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++ Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++ Sergey Platonov
 
Борис Сазонов, RAII потоки и CancellationToken в C++
Борис Сазонов, RAII потоки и CancellationToken в C++Борис Сазонов, RAII потоки и CancellationToken в C++
Борис Сазонов, RAII потоки и CancellationToken в C++Sergey Platonov
 
Использование юнит-тестов для повышения качества разработки
Использование юнит-тестов для повышения качества разработкиИспользование юнит-тестов для повышения качества разработки
Использование юнит-тестов для повышения качества разработкиvictor-yastrebov
 
Антон Полухин, Немного о Boost
Антон Полухин, Немного о BoostАнтон Полухин, Немного о Boost
Антон Полухин, Немного о BoostSergey Platonov
 
Современный статический анализ кода: что умеет он, чего не умели линтеры
Современный статический анализ кода: что умеет он, чего не умели линтерыСовременный статический анализ кода: что умеет он, чего не умели линтеры
Современный статический анализ кода: что умеет он, чего не умели линтерыcorehard_by
 
Григорий Демченко, Асинхронность и неблокирующая синхронизация
Григорий Демченко, Асинхронность и неблокирующая синхронизацияГригорий Демченко, Асинхронность и неблокирующая синхронизация
Григорий Демченко, Асинхронность и неблокирующая синхронизацияSergey Platonov
 
C++ Core Guidelines
C++ Core Guidelines C++ Core Guidelines
C++ Core Guidelines Sergey Zubkov
 
ПВТ - весна 2015 - Лекция 8. Многопоточное программирование без использования...
ПВТ - весна 2015 - Лекция 8. Многопоточное программирование без использования...ПВТ - весна 2015 - Лекция 8. Многопоточное программирование без использования...
ПВТ - весна 2015 - Лекция 8. Многопоточное программирование без использования...Alexey Paznikov
 
Никита Глушков, К вопросу о реализации кроссплатформенных фреймворков
Никита Глушков, К вопросу о реализации кроссплатформенных фреймворковНикита Глушков, К вопросу о реализации кроссплатформенных фреймворков
Никита Глушков, К вопросу о реализации кроссплатформенных фреймворковSergey Platonov
 
Догнать и перегнать boost::lexical_cast
Догнать и перегнать boost::lexical_castДогнать и перегнать boost::lexical_cast
Догнать и перегнать boost::lexical_castRoman Orlov
 
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...Alexey Paznikov
 
Intel IPP Samples for Windows - работа над ошибками
Intel IPP Samples for Windows - работа над ошибкамиIntel IPP Samples for Windows - работа над ошибками
Intel IPP Samples for Windows - работа над ошибкамиTatyanazaxarova
 
Андрей Карпов, Приватные байки от разработчиков анализатора кода
Андрей Карпов, Приватные байки от разработчиков анализатора кодаАндрей Карпов, Приватные байки от разработчиков анализатора кода
Андрей Карпов, Приватные байки от разработчиков анализатора кодаSergey Platonov
 
Кулагин И.И., Пазников А.А., Курносов М.Г. Оптимизация информационных обменов...
Кулагин И.И., Пазников А.А., Курносов М.Г. Оптимизация информационных обменов...Кулагин И.И., Пазников А.А., Курносов М.Г. Оптимизация информационных обменов...
Кулагин И.И., Пазников А.А., Курносов М.Г. Оптимизация информационных обменов...Alexey Paznikov
 
Шишки, набитые за 15 лет использования акторов в C++
Шишки, набитые за 15 лет использования акторов в C++Шишки, набитые за 15 лет использования акторов в C++
Шишки, набитые за 15 лет использования акторов в C++Yauheni Akhotnikau
 
разработка серверов и серверных приложений лекция №3
разработка серверов и серверных приложений лекция №3разработка серверов и серверных приложений лекция №3
разработка серверов и серверных приложений лекция №3Eugeniy Tyumentcev
 
Лекция 7: Многопоточное программирование: часть 3 (OpenMP)
Лекция 7: Многопоточное программирование: часть 3 (OpenMP)Лекция 7: Многопоточное программирование: часть 3 (OpenMP)
Лекция 7: Многопоточное программирование: часть 3 (OpenMP)Mikhail Kurnosov
 
ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. М...
ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. М...ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. М...
ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. М...Alexey Paznikov
 

Tendances (20)

Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++ Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
 
Борис Сазонов, RAII потоки и CancellationToken в C++
Борис Сазонов, RAII потоки и CancellationToken в C++Борис Сазонов, RAII потоки и CancellationToken в C++
Борис Сазонов, RAII потоки и CancellationToken в C++
 
Parallel STL
Parallel STLParallel STL
Parallel STL
 
Использование юнит-тестов для повышения качества разработки
Использование юнит-тестов для повышения качества разработкиИспользование юнит-тестов для повышения качества разработки
Использование юнит-тестов для повышения качества разработки
 
Антон Полухин, Немного о Boost
Антон Полухин, Немного о BoostАнтон Полухин, Немного о Boost
Антон Полухин, Немного о Boost
 
Современный статический анализ кода: что умеет он, чего не умели линтеры
Современный статический анализ кода: что умеет он, чего не умели линтерыСовременный статический анализ кода: что умеет он, чего не умели линтеры
Современный статический анализ кода: что умеет он, чего не умели линтеры
 
Григорий Демченко, Асинхронность и неблокирующая синхронизация
Григорий Демченко, Асинхронность и неблокирующая синхронизацияГригорий Демченко, Асинхронность и неблокирующая синхронизация
Григорий Демченко, Асинхронность и неблокирующая синхронизация
 
C++ Core Guidelines
C++ Core Guidelines C++ Core Guidelines
C++ Core Guidelines
 
ПВТ - весна 2015 - Лекция 8. Многопоточное программирование без использования...
ПВТ - весна 2015 - Лекция 8. Многопоточное программирование без использования...ПВТ - весна 2015 - Лекция 8. Многопоточное программирование без использования...
ПВТ - весна 2015 - Лекция 8. Многопоточное программирование без использования...
 
Никита Глушков, К вопросу о реализации кроссплатформенных фреймворков
Никита Глушков, К вопросу о реализации кроссплатформенных фреймворковНикита Глушков, К вопросу о реализации кроссплатформенных фреймворков
Никита Глушков, К вопросу о реализации кроссплатформенных фреймворков
 
Догнать и перегнать boost::lexical_cast
Догнать и перегнать boost::lexical_castДогнать и перегнать boost::lexical_cast
Догнать и перегнать boost::lexical_cast
 
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...
 
Intel IPP Samples for Windows - работа над ошибками
Intel IPP Samples for Windows - работа над ошибкамиIntel IPP Samples for Windows - работа над ошибками
Intel IPP Samples for Windows - работа над ошибками
 
Андрей Карпов, Приватные байки от разработчиков анализатора кода
Андрей Карпов, Приватные байки от разработчиков анализатора кодаАндрей Карпов, Приватные байки от разработчиков анализатора кода
Андрей Карпов, Приватные байки от разработчиков анализатора кода
 
Кулагин И.И., Пазников А.А., Курносов М.Г. Оптимизация информационных обменов...
Кулагин И.И., Пазников А.А., Курносов М.Г. Оптимизация информационных обменов...Кулагин И.И., Пазников А.А., Курносов М.Г. Оптимизация информационных обменов...
Кулагин И.И., Пазников А.А., Курносов М.Г. Оптимизация информационных обменов...
 
Шишки, набитые за 15 лет использования акторов в C++
Шишки, набитые за 15 лет использования акторов в C++Шишки, набитые за 15 лет использования акторов в C++
Шишки, набитые за 15 лет использования акторов в C++
 
Цена ошибки
Цена ошибкиЦена ошибки
Цена ошибки
 
разработка серверов и серверных приложений лекция №3
разработка серверов и серверных приложений лекция №3разработка серверов и серверных приложений лекция №3
разработка серверов и серверных приложений лекция №3
 
Лекция 7: Многопоточное программирование: часть 3 (OpenMP)
Лекция 7: Многопоточное программирование: часть 3 (OpenMP)Лекция 7: Многопоточное программирование: часть 3 (OpenMP)
Лекция 7: Многопоточное программирование: часть 3 (OpenMP)
 
ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. М...
ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. М...ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. М...
ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. М...
 

En vedette

Григорий Демченко, Универсальный адаптер
Григорий Демченко, Универсальный адаптерГригорий Демченко, Универсальный адаптер
Григорий Демченко, Универсальный адаптерSergey Platonov
 
Алексей Кутумов, C++ без исключений, часть 3
Алексей Кутумов,  C++ без исключений, часть 3Алексей Кутумов,  C++ без исключений, часть 3
Алексей Кутумов, C++ без исключений, часть 3Platonov Sergey
 
Evgeniy Muralev, Mark Vince, Working with the compiler, not against it
Evgeniy Muralev, Mark Vince, Working with the compiler, not against itEvgeniy Muralev, Mark Vince, Working with the compiler, not against it
Evgeniy Muralev, Mark Vince, Working with the compiler, not against itSergey Platonov
 
Фитнес для вашего кода: как держать его в форме
Фитнес для вашего кода: как держать его в формеФитнес для вашего кода: как держать его в форме
Фитнес для вашего кода: как держать его в формеIlia Shishkov
 
Антон Бикинеев, Reflection in C++Next
Антон Бикинеев,  Reflection in C++NextАнтон Бикинеев,  Reflection in C++Next
Антон Бикинеев, Reflection in C++NextSergey Platonov
 
Повседневный С++: алгоритмы и итераторы @ C++ Russia 2017
Повседневный С++: алгоритмы и итераторы @ C++ Russia 2017Повседневный С++: алгоритмы и итераторы @ C++ Russia 2017
Повседневный С++: алгоритмы и итераторы @ C++ Russia 2017Mikhail Matrosov
 
Fuzzing: The New Unit Testing
Fuzzing: The New Unit TestingFuzzing: The New Unit Testing
Fuzzing: The New Unit TestingDmitry Vyukov
 
Quality assurance of large c++ projects
Quality assurance of large c++ projectsQuality assurance of large c++ projects
Quality assurance of large c++ projectscorehard_by
 
Василий Сорокин, Простой REST сервер на Qt с рефлексией
Василий Сорокин, Простой REST сервер на Qt с рефлексиейВасилий Сорокин, Простой REST сервер на Qt с рефлексией
Василий Сорокин, Простой REST сервер на Qt с рефлексиейSergey Platonov
 
Павел Беликов, Опыт мигрирования крупного проекта с Windows-only на Linux
Павел Беликов, Опыт мигрирования крупного проекта с Windows-only на LinuxПавел Беликов, Опыт мигрирования крупного проекта с Windows-only на Linux
Павел Беликов, Опыт мигрирования крупного проекта с Windows-only на LinuxPlatonov Sergey
 
Евгений Крутько, Многопоточные вычисления, современный подход.
Евгений Крутько, Многопоточные вычисления, современный подход.Евгений Крутько, Многопоточные вычисления, современный подход.
Евгений Крутько, Многопоточные вычисления, современный подход.Platonov Sergey
 
Тененёв Анатолий, Boost.Asio в алгоритмической торговле
Тененёв Анатолий, Boost.Asio в алгоритмической торговлеТененёв Анатолий, Boost.Asio в алгоритмической торговле
Тененёв Анатолий, Boost.Asio в алгоритмической торговлеPlatonov Sergey
 
Антон Полухин. C++17
Антон Полухин. C++17Антон Полухин. C++17
Антон Полухин. C++17Sergey Platonov
 
Gor Nishanov, C++ Coroutines – a negative overhead abstraction
Gor Nishanov,  C++ Coroutines – a negative overhead abstractionGor Nishanov,  C++ Coroutines – a negative overhead abstraction
Gor Nishanov, C++ Coroutines – a negative overhead abstractionSergey Platonov
 
Повседневный С++: алгоритмы и итераторы
Повседневный С++: алгоритмы и итераторы Повседневный С++: алгоритмы и итераторы
Повседневный С++: алгоритмы и итераторы corehard_by
 
Дмитрий Кашицын, Вывод типов в динамических и не очень языках I
Дмитрий Кашицын, Вывод типов в динамических и не очень языках IДмитрий Кашицын, Вывод типов в динамических и не очень языках I
Дмитрий Кашицын, Вывод типов в динамических и не очень языках IPlatonov Sergey
 
Actor Model and C++: what, why and how?
Actor Model and C++: what, why and how?Actor Model and C++: what, why and how?
Actor Model and C++: what, why and how?Yauheni Akhotnikau
 
Kirk Shoop, Reactive programming in C++
Kirk Shoop, Reactive programming in C++Kirk Shoop, Reactive programming in C++
Kirk Shoop, Reactive programming in C++Sergey Platonov
 
Александр Гранин, Функциональная 'Жизнь': параллельные клеточные автоматы и к...
Александр Гранин, Функциональная 'Жизнь': параллельные клеточные автоматы и к...Александр Гранин, Функциональная 'Жизнь': параллельные клеточные автоматы и к...
Александр Гранин, Функциональная 'Жизнь': параллельные клеточные автоматы и к...Sergey Platonov
 

En vedette (20)

Григорий Демченко, Универсальный адаптер
Григорий Демченко, Универсальный адаптерГригорий Демченко, Универсальный адаптер
Григорий Демченко, Универсальный адаптер
 
Clang tidy
Clang tidyClang tidy
Clang tidy
 
Алексей Кутумов, C++ без исключений, часть 3
Алексей Кутумов,  C++ без исключений, часть 3Алексей Кутумов,  C++ без исключений, часть 3
Алексей Кутумов, C++ без исключений, часть 3
 
Evgeniy Muralev, Mark Vince, Working with the compiler, not against it
Evgeniy Muralev, Mark Vince, Working with the compiler, not against itEvgeniy Muralev, Mark Vince, Working with the compiler, not against it
Evgeniy Muralev, Mark Vince, Working with the compiler, not against it
 
Фитнес для вашего кода: как держать его в форме
Фитнес для вашего кода: как держать его в формеФитнес для вашего кода: как держать его в форме
Фитнес для вашего кода: как держать его в форме
 
Антон Бикинеев, Reflection in C++Next
Антон Бикинеев,  Reflection in C++NextАнтон Бикинеев,  Reflection in C++Next
Антон Бикинеев, Reflection in C++Next
 
Повседневный С++: алгоритмы и итераторы @ C++ Russia 2017
Повседневный С++: алгоритмы и итераторы @ C++ Russia 2017Повседневный С++: алгоритмы и итераторы @ C++ Russia 2017
Повседневный С++: алгоритмы и итераторы @ C++ Russia 2017
 
Fuzzing: The New Unit Testing
Fuzzing: The New Unit TestingFuzzing: The New Unit Testing
Fuzzing: The New Unit Testing
 
Quality assurance of large c++ projects
Quality assurance of large c++ projectsQuality assurance of large c++ projects
Quality assurance of large c++ projects
 
Василий Сорокин, Простой REST сервер на Qt с рефлексией
Василий Сорокин, Простой REST сервер на Qt с рефлексиейВасилий Сорокин, Простой REST сервер на Qt с рефлексией
Василий Сорокин, Простой REST сервер на Qt с рефлексией
 
Павел Беликов, Опыт мигрирования крупного проекта с Windows-only на Linux
Павел Беликов, Опыт мигрирования крупного проекта с Windows-only на LinuxПавел Беликов, Опыт мигрирования крупного проекта с Windows-only на Linux
Павел Беликов, Опыт мигрирования крупного проекта с Windows-only на Linux
 
Евгений Крутько, Многопоточные вычисления, современный подход.
Евгений Крутько, Многопоточные вычисления, современный подход.Евгений Крутько, Многопоточные вычисления, современный подход.
Евгений Крутько, Многопоточные вычисления, современный подход.
 
Тененёв Анатолий, Boost.Asio в алгоритмической торговле
Тененёв Анатолий, Boost.Asio в алгоритмической торговлеТененёв Анатолий, Boost.Asio в алгоритмической торговле
Тененёв Анатолий, Boost.Asio в алгоритмической торговле
 
Антон Полухин. C++17
Антон Полухин. C++17Антон Полухин. C++17
Антон Полухин. C++17
 
Gor Nishanov, C++ Coroutines – a negative overhead abstraction
Gor Nishanov,  C++ Coroutines – a negative overhead abstractionGor Nishanov,  C++ Coroutines – a negative overhead abstraction
Gor Nishanov, C++ Coroutines – a negative overhead abstraction
 
Повседневный С++: алгоритмы и итераторы
Повседневный С++: алгоритмы и итераторы Повседневный С++: алгоритмы и итераторы
Повседневный С++: алгоритмы и итераторы
 
Дмитрий Кашицын, Вывод типов в динамических и не очень языках I
Дмитрий Кашицын, Вывод типов в динамических и не очень языках IДмитрий Кашицын, Вывод типов в динамических и не очень языках I
Дмитрий Кашицын, Вывод типов в динамических и не очень языках I
 
Actor Model and C++: what, why and how?
Actor Model and C++: what, why and how?Actor Model and C++: what, why and how?
Actor Model and C++: what, why and how?
 
Kirk Shoop, Reactive programming in C++
Kirk Shoop, Reactive programming in C++Kirk Shoop, Reactive programming in C++
Kirk Shoop, Reactive programming in C++
 
Александр Гранин, Функциональная 'Жизнь': параллельные клеточные автоматы и к...
Александр Гранин, Функциональная 'Жизнь': параллельные клеточные автоматы и к...Александр Гранин, Функциональная 'Жизнь': параллельные клеточные автоматы и к...
Александр Гранин, Функциональная 'Жизнь': параллельные клеточные автоматы и к...
 

Similaire à Для чего мы делали свой акторный фреймворк и что из этого вышло?

шишки, набитые за 15 лет использования акторов в c++ v.001.3
шишки, набитые за 15 лет использования акторов в c++ v.001.3шишки, набитые за 15 лет использования акторов в c++ v.001.3
шишки, набитые за 15 лет использования акторов в c++ v.001.3corehard_by
 
В поисках эффективного middleware
В поисках эффективного middlewareВ поисках эффективного middleware
В поисках эффективного middlewareAlexander Gerasiov
 
Как жить в согласии с SOLID?
Как жить в согласии с SOLID?Как жить в согласии с SOLID?
Как жить в согласии с SOLID?etyumentcev
 
Разработка надежных параллельных, распределенных приложений: быстро и дешево
Разработка надежных параллельных, распределенных приложений: быстро и дешевоРазработка надежных параллельных, распределенных приложений: быстро и дешево
Разработка надежных параллельных, распределенных приложений: быстро и дешевоDotNetConf
 
Сетевой инженер 2.0. Что нужно знать о программируемости в корпоративной сети?
Сетевой инженер 2.0. Что нужно знать о программируемости в корпоративной сети?Сетевой инженер 2.0. Что нужно знать о программируемости в корпоративной сети?
Сетевой инженер 2.0. Что нужно знать о программируемости в корпоративной сети?Cisco Russia
 
Erlang для высоконагруженных систем (Валентин Нечаев)
Erlang для высоконагруженных систем (Валентин Нечаев)Erlang для высоконагруженных систем (Валентин Нечаев)
Erlang для высоконагруженных систем (Валентин Нечаев)Ontico
 
Actors for fun and profit
Actors for fun and profitActors for fun and profit
Actors for fun and profitcorehard_by
 
My talk on DevOps engineer's adventures in the Windows world at UWDC 2017
My talk on DevOps engineer's adventures in the Windows world at UWDC 2017My talk on DevOps engineer's adventures in the Windows world at UWDC 2017
My talk on DevOps engineer's adventures in the Windows world at UWDC 2017Alex Chistyakov
 
Windows Azure & NodeJS Microsoft SWIT 2012
Windows Azure & NodeJS Microsoft SWIT 2012 Windows Azure & NodeJS Microsoft SWIT 2012
Windows Azure & NodeJS Microsoft SWIT 2012 Dmytro Mindra
 
Powershell, Graphs and more. Or how to find dependencies in your systems
Powershell, Graphs and more. Or how to find dependencies in your systemsPowershell, Graphs and more. Or how to find dependencies in your systems
Powershell, Graphs and more. Or how to find dependencies in your systemsAndrey Vernigora
 
Другая виртуализация
Другая виртуализацияДругая виртуализация
Другая виртуализацияYandex
 
Windows Azure and node js
Windows Azure and node jsWindows Azure and node js
Windows Azure and node jsAlex Tumanoff
 
микроСЕРВИСЫ: огонь, вода и медные трубы
микроСЕРВИСЫ: огонь, вода и медные трубымикроСЕРВИСЫ: огонь, вода и медные трубы
микроСЕРВИСЫ: огонь, вода и медные трубыAleksandr Tarasov
 
UAFPUG6 - PureMVC
UAFPUG6 - PureMVCUAFPUG6 - PureMVC
UAFPUG6 - PureMVCmandrew182
 
Uafpug 8 Presentation Puremvc Papervision Gallery Kuriksha Dmitry
Uafpug 8 Presentation Puremvc Papervision Gallery Kuriksha DmitryUafpug 8 Presentation Puremvc Papervision Gallery Kuriksha Dmitry
Uafpug 8 Presentation Puremvc Papervision Gallery Kuriksha DmitryMax Rozdobudko
 
64-битная версия Loki
64-битная версия Loki64-битная версия Loki
64-битная версия LokiTatyanazaxarova
 
Принципы работы статического анализатора кода PVS-Studio
Принципы работы статического анализатора кода PVS-StudioПринципы работы статического анализатора кода PVS-Studio
Принципы работы статического анализатора кода PVS-StudioAndrey Karpov
 

Similaire à Для чего мы делали свой акторный фреймворк и что из этого вышло? (20)

шишки, набитые за 15 лет использования акторов в c++ v.001.3
шишки, набитые за 15 лет использования акторов в c++ v.001.3шишки, набитые за 15 лет использования акторов в c++ v.001.3
шишки, набитые за 15 лет использования акторов в c++ v.001.3
 
Обзор SObjectizer 5.5
Обзор SObjectizer 5.5Обзор SObjectizer 5.5
Обзор SObjectizer 5.5
 
В поисках эффективного middleware
В поисках эффективного middlewareВ поисках эффективного middleware
В поисках эффективного middleware
 
Как жить в согласии с SOLID?
Как жить в согласии с SOLID?Как жить в согласии с SOLID?
Как жить в согласии с SOLID?
 
Разработка надежных параллельных, распределенных приложений: быстро и дешево
Разработка надежных параллельных, распределенных приложений: быстро и дешевоРазработка надежных параллельных, распределенных приложений: быстро и дешево
Разработка надежных параллельных, распределенных приложений: быстро и дешево
 
Сетевой инженер 2.0. Что нужно знать о программируемости в корпоративной сети?
Сетевой инженер 2.0. Что нужно знать о программируемости в корпоративной сети?Сетевой инженер 2.0. Что нужно знать о программируемости в корпоративной сети?
Сетевой инженер 2.0. Что нужно знать о программируемости в корпоративной сети?
 
Erlang для высоконагруженных систем (Валентин Нечаев)
Erlang для высоконагруженных систем (Валентин Нечаев)Erlang для высоконагруженных систем (Валентин Нечаев)
Erlang для высоконагруженных систем (Валентин Нечаев)
 
Actors for fun and profit
Actors for fun and profitActors for fun and profit
Actors for fun and profit
 
My talk on DevOps engineer's adventures in the Windows world at UWDC 2017
My talk on DevOps engineer's adventures in the Windows world at UWDC 2017My talk on DevOps engineer's adventures in the Windows world at UWDC 2017
My talk on DevOps engineer's adventures in the Windows world at UWDC 2017
 
Deep storm presentation
Deep storm presentationDeep storm presentation
Deep storm presentation
 
Windows Azure & NodeJS Microsoft SWIT 2012
Windows Azure & NodeJS Microsoft SWIT 2012 Windows Azure & NodeJS Microsoft SWIT 2012
Windows Azure & NodeJS Microsoft SWIT 2012
 
Powershell, Graphs and more. Or how to find dependencies in your systems
Powershell, Graphs and more. Or how to find dependencies in your systemsPowershell, Graphs and more. Or how to find dependencies in your systems
Powershell, Graphs and more. Or how to find dependencies in your systems
 
Другая виртуализация
Другая виртуализацияДругая виртуализация
Другая виртуализация
 
Windows Azure and node js
Windows Azure and node jsWindows Azure and node js
Windows Azure and node js
 
микроСЕРВИСЫ: огонь, вода и медные трубы
микроСЕРВИСЫ: огонь, вода и медные трубымикроСЕРВИСЫ: огонь, вода и медные трубы
микроСЕРВИСЫ: огонь, вода и медные трубы
 
UAFPUG6 - PureMVC
UAFPUG6 - PureMVCUAFPUG6 - PureMVC
UAFPUG6 - PureMVC
 
Uafpug 8 Presentation Puremvc Papervision Gallery Kuriksha Dmitry
Uafpug 8 Presentation Puremvc Papervision Gallery Kuriksha DmitryUafpug 8 Presentation Puremvc Papervision Gallery Kuriksha Dmitry
Uafpug 8 Presentation Puremvc Papervision Gallery Kuriksha Dmitry
 
PureMVC and Papervision
PureMVC and PapervisionPureMVC and Papervision
PureMVC and Papervision
 
64-битная версия Loki
64-битная версия Loki64-битная версия Loki
64-битная версия Loki
 
Принципы работы статического анализатора кода PVS-Studio
Принципы работы статического анализатора кода PVS-StudioПринципы работы статического анализатора кода PVS-Studio
Принципы работы статического анализатора кода PVS-Studio
 

Plus de Yauheni Akhotnikau

arataga. SObjectizer and RESTinio in action: a real-world example
arataga. SObjectizer and RESTinio in action: a real-world examplearataga. SObjectizer and RESTinio in action: a real-world example
arataga. SObjectizer and RESTinio in action: a real-world exampleYauheni Akhotnikau
 
Actor Model and C++: what, why and how? (March 2020 Edition)
Actor Model and C++: what, why and how? (March 2020 Edition)Actor Model and C++: what, why and how? (March 2020 Edition)
Actor Model and C++: what, why and how? (March 2020 Edition)Yauheni Akhotnikau
 
What is SObjectizer 5.7 (at v.5.7.0)
What is SObjectizer 5.7 (at v.5.7.0)What is SObjectizer 5.7 (at v.5.7.0)
What is SObjectizer 5.7 (at v.5.7.0)Yauheni Akhotnikau
 
What is SObjectizer 5.6 (at v.5.6.0)
What is SObjectizer 5.6 (at v.5.6.0)What is SObjectizer 5.6 (at v.5.6.0)
What is SObjectizer 5.6 (at v.5.6.0)Yauheni Akhotnikau
 
[C++ CoreHard Autumn 2018] Actors vs CSP vs Task...
[C++ CoreHard Autumn 2018] Actors vs CSP vs Task...[C++ CoreHard Autumn 2018] Actors vs CSP vs Task...
[C++ CoreHard Autumn 2018] Actors vs CSP vs Task...Yauheni Akhotnikau
 
Shrimp: A Rather Practical Example Of Application Development With RESTinio a...
Shrimp: A Rather Practical Example Of Application Development With RESTinio a...Shrimp: A Rather Practical Example Of Application Development With RESTinio a...
Shrimp: A Rather Practical Example Of Application Development With RESTinio a...Yauheni Akhotnikau
 
Акторы в C++: взгляд старого практикующего актородела (St. Petersburg C++ Use...
Акторы в C++: взгляд старого практикующего актородела (St. Petersburg C++ Use...Акторы в C++: взгляд старого практикующего актородела (St. Petersburg C++ Use...
Акторы в C++: взгляд старого практикующего актородела (St. Petersburg C++ Use...Yauheni Akhotnikau
 
Акторы на C++: стоило ли оно того?
Акторы на C++: стоило ли оно того?Акторы на C++: стоило ли оно того?
Акторы на C++: стоило ли оно того?Yauheni Akhotnikau
 
25 Years of C++ History Flashed in Front of My Eyes
25 Years of C++ History Flashed in Front of My Eyes25 Years of C++ History Flashed in Front of My Eyes
25 Years of C++ History Flashed in Front of My EyesYauheni Akhotnikau
 
GECon 2017: C++ - a Monster that no one likes but that will outlast them all
GECon 2017: C++ - a Monster that no one likes but that will outlast them allGECon 2017: C++ - a Monster that no one likes but that will outlast them all
GECon 2017: C++ - a Monster that no one likes but that will outlast them allYauheni Akhotnikau
 
Dive into SObjectizer 5.5. Tenth part: Mutable Messages
Dive into SObjectizer 5.5. Tenth part: Mutable MessagesDive into SObjectizer 5.5. Tenth part: Mutable Messages
Dive into SObjectizer 5.5. Tenth part: Mutable MessagesYauheni Akhotnikau
 
Модель акторов и C++ что, зачем и как?
Модель акторов и C++ что, зачем и как?Модель акторов и C++ что, зачем и как?
Модель акторов и C++ что, зачем и как?Yauheni Akhotnikau
 
Dive into SObjectizer 5.5. Ninth Part: Message Chains
Dive into SObjectizer 5.5. Ninth Part: Message ChainsDive into SObjectizer 5.5. Ninth Part: Message Chains
Dive into SObjectizer 5.5. Ninth Part: Message ChainsYauheni Akhotnikau
 
Dive into SObjectizer 5.5. Eighth Part: Dispatchers
Dive into SObjectizer 5.5. Eighth Part: DispatchersDive into SObjectizer 5.5. Eighth Part: Dispatchers
Dive into SObjectizer 5.5. Eighth Part: DispatchersYauheni Akhotnikau
 
What's new in SObjectizer 5.5.9
What's new in SObjectizer 5.5.9What's new in SObjectizer 5.5.9
What's new in SObjectizer 5.5.9Yauheni Akhotnikau
 
Dive into SObjectizer 5.5. Seventh part: Message Limits
Dive into SObjectizer 5.5. Seventh part: Message LimitsDive into SObjectizer 5.5. Seventh part: Message Limits
Dive into SObjectizer 5.5. Seventh part: Message LimitsYauheni Akhotnikau
 
Dive into SObjectizer-5.5. Sixth part: Synchronous Interaction
Dive into SObjectizer-5.5. Sixth part: Synchronous InteractionDive into SObjectizer-5.5. Sixth part: Synchronous Interaction
Dive into SObjectizer-5.5. Sixth part: Synchronous InteractionYauheni Akhotnikau
 
Dive into SObjectizer 5.5. Fifth part: Timers
Dive into SObjectizer 5.5. Fifth part: TimersDive into SObjectizer 5.5. Fifth part: Timers
Dive into SObjectizer 5.5. Fifth part: TimersYauheni Akhotnikau
 
What’s new in SObjectizer 5.5.8
What’s new in SObjectizer 5.5.8What’s new in SObjectizer 5.5.8
What’s new in SObjectizer 5.5.8Yauheni Akhotnikau
 
Dive into SObjectizer 5.5. Fourth part. Exception
Dive into SObjectizer 5.5. Fourth part. ExceptionDive into SObjectizer 5.5. Fourth part. Exception
Dive into SObjectizer 5.5. Fourth part. ExceptionYauheni Akhotnikau
 

Plus de Yauheni Akhotnikau (20)

arataga. SObjectizer and RESTinio in action: a real-world example
arataga. SObjectizer and RESTinio in action: a real-world examplearataga. SObjectizer and RESTinio in action: a real-world example
arataga. SObjectizer and RESTinio in action: a real-world example
 
Actor Model and C++: what, why and how? (March 2020 Edition)
Actor Model and C++: what, why and how? (March 2020 Edition)Actor Model and C++: what, why and how? (March 2020 Edition)
Actor Model and C++: what, why and how? (March 2020 Edition)
 
What is SObjectizer 5.7 (at v.5.7.0)
What is SObjectizer 5.7 (at v.5.7.0)What is SObjectizer 5.7 (at v.5.7.0)
What is SObjectizer 5.7 (at v.5.7.0)
 
What is SObjectizer 5.6 (at v.5.6.0)
What is SObjectizer 5.6 (at v.5.6.0)What is SObjectizer 5.6 (at v.5.6.0)
What is SObjectizer 5.6 (at v.5.6.0)
 
[C++ CoreHard Autumn 2018] Actors vs CSP vs Task...
[C++ CoreHard Autumn 2018] Actors vs CSP vs Task...[C++ CoreHard Autumn 2018] Actors vs CSP vs Task...
[C++ CoreHard Autumn 2018] Actors vs CSP vs Task...
 
Shrimp: A Rather Practical Example Of Application Development With RESTinio a...
Shrimp: A Rather Practical Example Of Application Development With RESTinio a...Shrimp: A Rather Practical Example Of Application Development With RESTinio a...
Shrimp: A Rather Practical Example Of Application Development With RESTinio a...
 
Акторы в C++: взгляд старого практикующего актородела (St. Petersburg C++ Use...
Акторы в C++: взгляд старого практикующего актородела (St. Petersburg C++ Use...Акторы в C++: взгляд старого практикующего актородела (St. Petersburg C++ Use...
Акторы в C++: взгляд старого практикующего актородела (St. Petersburg C++ Use...
 
Акторы на C++: стоило ли оно того?
Акторы на C++: стоило ли оно того?Акторы на C++: стоило ли оно того?
Акторы на C++: стоило ли оно того?
 
25 Years of C++ History Flashed in Front of My Eyes
25 Years of C++ History Flashed in Front of My Eyes25 Years of C++ History Flashed in Front of My Eyes
25 Years of C++ History Flashed in Front of My Eyes
 
GECon 2017: C++ - a Monster that no one likes but that will outlast them all
GECon 2017: C++ - a Monster that no one likes but that will outlast them allGECon 2017: C++ - a Monster that no one likes but that will outlast them all
GECon 2017: C++ - a Monster that no one likes but that will outlast them all
 
Dive into SObjectizer 5.5. Tenth part: Mutable Messages
Dive into SObjectizer 5.5. Tenth part: Mutable MessagesDive into SObjectizer 5.5. Tenth part: Mutable Messages
Dive into SObjectizer 5.5. Tenth part: Mutable Messages
 
Модель акторов и C++ что, зачем и как?
Модель акторов и C++ что, зачем и как?Модель акторов и C++ что, зачем и как?
Модель акторов и C++ что, зачем и как?
 
Dive into SObjectizer 5.5. Ninth Part: Message Chains
Dive into SObjectizer 5.5. Ninth Part: Message ChainsDive into SObjectizer 5.5. Ninth Part: Message Chains
Dive into SObjectizer 5.5. Ninth Part: Message Chains
 
Dive into SObjectizer 5.5. Eighth Part: Dispatchers
Dive into SObjectizer 5.5. Eighth Part: DispatchersDive into SObjectizer 5.5. Eighth Part: Dispatchers
Dive into SObjectizer 5.5. Eighth Part: Dispatchers
 
What's new in SObjectizer 5.5.9
What's new in SObjectizer 5.5.9What's new in SObjectizer 5.5.9
What's new in SObjectizer 5.5.9
 
Dive into SObjectizer 5.5. Seventh part: Message Limits
Dive into SObjectizer 5.5. Seventh part: Message LimitsDive into SObjectizer 5.5. Seventh part: Message Limits
Dive into SObjectizer 5.5. Seventh part: Message Limits
 
Dive into SObjectizer-5.5. Sixth part: Synchronous Interaction
Dive into SObjectizer-5.5. Sixth part: Synchronous InteractionDive into SObjectizer-5.5. Sixth part: Synchronous Interaction
Dive into SObjectizer-5.5. Sixth part: Synchronous Interaction
 
Dive into SObjectizer 5.5. Fifth part: Timers
Dive into SObjectizer 5.5. Fifth part: TimersDive into SObjectizer 5.5. Fifth part: Timers
Dive into SObjectizer 5.5. Fifth part: Timers
 
What’s new in SObjectizer 5.5.8
What’s new in SObjectizer 5.5.8What’s new in SObjectizer 5.5.8
What’s new in SObjectizer 5.5.8
 
Dive into SObjectizer 5.5. Fourth part. Exception
Dive into SObjectizer 5.5. Fourth part. ExceptionDive into SObjectizer 5.5. Fourth part. Exception
Dive into SObjectizer 5.5. Fourth part. Exception
 

Для чего мы делали свой акторный фреймворк и что из этого вышло?

  • 1. C++ Russia 2017 Для чего мы делали свой акторный фреймворк и что из этого вышло? Евгений Охотников
  • 2. А делали ли мы акторный фреймворк вообще? ? 2
  • 3. Давным-давно, в далекой-далекой... 1994-й год. Гомель. КБ Системного Программирования. Отдел АСУТП. Попытка сделать объектно-ориентированную SCADA-систему. SCADA - Supervisory Control And Data Acquisition 3
  • 4. О SCADA-системах в двух словах 4 http://www.scadasoftware.net/
  • 5. Объектная SCADA. Зачем? Традиционный подход SCADA-систем в те времена: ● набор нумерованных или именованных каналов ввода-вывода (тегов); ● слабая структуризация и почти отсутствие декомпозиции; ● трудозатраты растут с увеличением числа тегов; ● трудозатраты еще быстрее растут по мере усложнения логики автоматизируемого техпроцесса. Мы хотели устранить это за счет декомпозиции на объекты, обладающие состоянием и поведением. 5
  • 6. Объектная SCADA. Что получилось? Агенты: ● конечные автоматы с явно выделенными состояниями; ● состояния агентов видны снаружи. 6
  • 7. Объектная SCADA. Что получилось? Взаимодействие через асинхронные сообщения: ● прицел на soft real-time; ● распространение информации как в режиме 1:1, так и в режиме 1:N. 7
  • 8. Объектная SCADA. Что получилось? Диспетчер. Отвечал за обработку очередей сообщений с учетом приоритетов и требований soft real-time. 8
  • 9. Объектная SCADA. Итог SCADA Objectizer. 1998-й год. Опробован в реальном проекте. Прекратил свое существование в начале 2000-го :( 9
  • 10. Предпосылки к перерождению Начало 2002-го. Компания "Интервэйл". Двое участников разработки SCADA Objectizer. Небольшая GUI-программа для управления подключенными к ПК устройствами... 10
  • 11. Четвертое пришествие Апрель 2002-го года. SObjectizer = SCADA + Objectizer. SObjectizer-4 (в четвертый раз все сначала). 11
  • 12. Этапы большого пути Май 2002-го: начало использования SO-4 в разработке. Март 2006-го: перевод SO-4 в категорию OpenSource проектов под BSD-лицензией. Сентябрь 2010-го: начало работ над SObjectizer-5. Май 2013-го: публичный релиз SO-5 под BSD-лицензией. Июль 2013-го: SObjectizer начал жить независимо от "Интервэйл". 12
  • 13. На данный момент... Последняя стабильная версия SObjectizer-5.5.18 – это: ● 25KLOC кода (+28KLOC кода в тестах +10KLOC кода в примерах); ● работа на платформах Windows, Linux, FreeBSD, MacOS, Android (через CrystaX NDK); ● поддержка компиляторов VC++ 12.0-14.0, GCC 4.8-6.3, clang 3.5-3.9; ● документация, презентации, статьи; ● отсутствие больших ломающих изменений с октября 2014-го. 13
  • 14. Тем не менее... Для чего же мы делали свой фреймворк? 14
  • 15. Как перестать бояться и... ...полюбить многопоточность? Голые thread, mutex и condition_variable – это пот, кровь и боль. Асинхронный обмен сообщениями рулит! И бибикает :) Ибо shared nothing и вот этот вот все. 15
  • 16. Бонусы асинхронного обмена сообщениями ● у каждого агента свое изолированное состояние (принцип shared nothing), упрощает жизнь в многопоточном коде; ● обмен сообщениями – естественный подход к решению некоторых типов задач; ● слабая связность между компонентами; ● очень простая работа с таймерами (отложенные и периодические сообщения); ● низкий порог входа для новичков. 16
  • 17. Давайте посмотрим на SObjectizer-5 с более близкого расстояния 17
  • 18. В SObjectizer есть сообщения Все взаимодействие между агентами в SObjectizer идет только через асинхронные сообщения. Сообщения это объекты. Тип сообщения наследуется от so_5::message_t. 18
  • 19. Выглядят сообщения вот так: struct request : public so_5::message_t { std::int64_t id_; std::map<std::string, std::string> params_; std::vector<std::uint8_t> payload_; std::chrono::steady_clock::timepoint deadline_; request( std::int64_t id, std::map<std::string, std::string> params, std::vector<std::uint8_t> payload, std::chrono::steady_clock::timeout deadline) : id_(id), params_(std::move(params)), payload_(std::move(payload)), deadline_(deadline) {} }; struct get_status : public so_5::signal_t {}; 19
  • 20. Отсылаются сообщения вот так: // Безотлагательная отсылка сообщения. so_5::send<request>(target, // Все это через perfect-forwarding идет в конструктор request-а. make_id(), make_params(), make_payload(), calculate_deadline()); // Безотлагательная отсылка сигнала. so_5::send<get_status>(target); // Отсылка сигнала с задержкой на 250ms. so_5::send_delayed<get_status>(target, std::chrono::milliseconds(250)); // Периодическая отсылка сигнала со стартовой задержкой в 250ms // и периодом повтора в 750ms. auto timer = so_5::send_periodic<get_status>(target, std::chrono::milliseconds(250), std::chrono::milliseconds(750)); 20
  • 21. Что такое Target для send? В "традиционной" Модели Акторов адресатом сообщения является актор. В SObjectizer сообщения отсылаются в "почтовый ящик". Почтовый ящик в SObjectizer называется mbox. 21
  • 22. Подписка на сообщение из mbox-а Для получения сообщения из mbox-а нужно выполнить подписку. Только после этого сообщения будут доставляться агенту. Подписку можно отменить. Сообщения доставляться перестанут. Подписки агента автоматически удаляются, когда агент уничтожается. Сообщения диспетчируются по типу, а не по содержимому. 22
  • 24. Multi-Producer/Single-Consumer Mbox Кто угодно может оправить. Подписаться может только один агент. MPSC-mbox создается для каждого агента автоматически. Использование MPSC-mbox-ов дает максимально близкое приближение к Модели Акторов. 24
  • 25. Multi-Producer/Multi-Consumer Mbox Кто угодно может оправить. Получат все подписчики. MPMC-mbox нужно создавать вручную (можно с именем, можно без). Простейший вариант модели Publish/Subscribe. 25
  • 26. В SObjectizer есть диспетчеры Именно диспетчер определяет где и когда агент будет обрабатывать свои сообщения. Каждый агент в SObjectizer должен быть привязан к конкретному диспетчеру. В приложении может быть запущено сразу несколько диспетчеров. 26
  • 27. Примеры диспетчеров one_thread: все агенты на одной и той же нити, все агенты используют одну очередь сообщений. active_obj: у каждого агента своя собственная нить, у каждого агента собственная очередь сообщений. adv_thread_pool: агенты на группе нитей, агент может мигрировать с одной нити на другую, события одного агента могут быть запущены параллельно, если они отмечены как thread_safe. Очереди сообщений могут быть персональными или общими для нескольких агентов. 27
  • 28. Больше диспетчеров, хороших и разных Всего в SObjectizer-5 "из коробки" доступно восемь типов диспетчеров: active_group active_obj adv_thread_pool one_thread prio_dedicated_threads::one_per_prio prio_one_thread::quoted_round_robin prio_one_thread::strictly_ordered thread_pool 28
  • 30. В SObjectizer есть агенты Как правило, реализуются как объекты классов, унаследованных от so_5::agent_t. class hello_world final : public so_5::agent_t { public : using so_5::agent_t::agent_t; virtual void so_evt_start() override { std::cout << "Hello, World!" << std::endl; so_deregister_agent_coop_normally(); } }; 30
  • 31. Агенты – это конечные автоматы 1. Мы не поддерживаем агентов в виде сопрограмм. Поэтому должен использоваться механизм callback-ов, чтобы отобразить N агентов на M рабочих нитей. 2. Ноги у SObjectizer растут из мира АСУТП, там конечные автоматы – это обычное дело. 3. Когда есть ярко выраженные состояния и разное поведение в каждом из состояний конечный автомат удобнее, чем вызовы receive с последующим выбором обработчика. 31
  • 33. Агент blinking_led (код) class blinking_led final : public so_5::agent_t { state_t off{ this }, blinking{ this }, blink_on{ initial_substate_of{ blinking } }, blink_off{ substate_of{ blinking } }; public : struct turn_on_off : public so_5::signal_t {}; blinking_led( context_t ctx ) : so_5::agent_t{ std::move(ctx) } { this >>= off; off.just_switch_to< turn_on_off >( blinking ); blinking.just_switch_to< turn_on_off >( off ); blink_on .on_enter( []{ std::cout << "ON" << std::endl; } ) .on_exit( []{ std::cout << "off" << std::endl; } ) .time_limit( std::chrono::milliseconds{1500}, blink_off ); blink_off .time_limit( std::chrono::milliseconds{750}, blink_on ); } }; 33
  • 34. Агент blinking_led (пояснения) class blinking_led final : public so_5::agent_t { state_t off{ this }, blinking{ this }, blink_on{ initial_substate_of{ blinking } }, blink_off{ substate_of{ blinking } }; public : struct turn_on_off : public so_5::signal_t {}; blinking_led( context_t ctx ) : so_5::agent_t{ std::move(ctx) } { this >>= off; off.just_switch_to< turn_on_off >( blinking ); blinking.just_switch_to< turn_on_off >( off ); blink_on .on_enter( []{ std::cout << "ON" << std::endl; } ) .on_exit( []{ std::cout << "off" << std::endl; } ) .time_limit( std::chrono::milliseconds{1500}, blink_off ); blink_off .time_limit( std::chrono::milliseconds{750}, blink_on ); } }; 34 Определение двух верхнеуровневых состояний.
  • 35. Агент blinking_led (пояснения) class blinking_led final : public so_5::agent_t { state_t off{ this }, blinking{ this }, blink_on{ initial_substate_of{ blinking } }, blink_off{ substate_of{ blinking } }; public : struct turn_on_off : public so_5::signal_t {}; blinking_led( context_t ctx ) : so_5::agent_t{ std::move(ctx) } { this >>= off; off.just_switch_to< turn_on_off >( blinking ); blinking.just_switch_to< turn_on_off >( off ); blink_on .on_enter( []{ std::cout << "ON" << std::endl; } ) .on_exit( []{ std::cout << "off" << std::endl; } ) .time_limit( std::chrono::milliseconds{1500}, blink_off ); blink_off .time_limit( std::chrono::milliseconds{750}, blink_on ); } }; 35 Определение двух вложенных состояний для состояния blinking. Подсостояние blink_on является начальным подсостоянием.
  • 36. Агент blinking_led (пояснения) class blinking_led final : public so_5::agent_t { state_t off{ this }, blinking{ this }, blink_on{ initial_substate_of{ blinking } }, blink_off{ substate_of{ blinking } }; public : struct turn_on_off : public so_5::signal_t {}; blinking_led( context_t ctx ) : so_5::agent_t{ std::move(ctx) } { this >>= off; off.just_switch_to< turn_on_off >( blinking ); blinking.just_switch_to< turn_on_off >( off ); blink_on .on_enter( []{ std::cout << "ON" << std::endl; } ) .on_exit( []{ std::cout << "off" << std::endl; } ) .time_limit( std::chrono::milliseconds{1500}, blink_off ); blink_off .time_limit( std::chrono::milliseconds{750}, blink_on ); } }; 36 Перевод агента в то состояние, в котором он должен начать свою работу. Кому не нравится перегрузка >>= для смены состояния агента, для тех есть альтернативный синтаксис: off.activate(); so_change_state(off);
  • 37. Агент blinking_led (пояснения) class blinking_led final : public so_5::agent_t { state_t off{ this }, blinking{ this }, blink_on{ initial_substate_of{ blinking } }, blink_off{ substate_of{ blinking } }; public : struct turn_on_off : public so_5::signal_t {}; blinking_led( context_t ctx ) : so_5::agent_t{ std::move(ctx) } { this >>= off; off.just_switch_to< turn_on_off >( blinking ); blinking.just_switch_to< turn_on_off >( off ); blink_on .on_enter( []{ std::cout << "ON" << std::endl; } ) .on_exit( []{ std::cout << "off" << std::endl; } ) .time_limit( std::chrono::milliseconds{1500}, blink_off ); blink_off .time_limit( std::chrono::milliseconds{750}, blink_on ); } }; 37 Реакция на сигнал включения и выключения для верхних состояний. Достаточно просто перейти в другое состояние.
  • 38. Агент blinking_led (пояснения) class blinking_led final : public so_5::agent_t { state_t off{ this }, blinking{ this }, blink_on{ initial_substate_of{ blinking } }, blink_off{ substate_of{ blinking } }; public : struct turn_on_off : public so_5::signal_t {}; blinking_led( context_t ctx ) : so_5::agent_t{ std::move(ctx) } { this >>= off; off.just_switch_to< turn_on_off >( blinking ); blinking.just_switch_to< turn_on_off >( off ); blink_on .on_enter( []{ std::cout << "ON" << std::endl; } ) .on_exit( []{ std::cout << "off" << std::endl; } ) .time_limit( std::chrono::milliseconds{1500}, blink_off ); blink_off .time_limit( std::chrono::milliseconds{750}, blink_on ); } }; 38 Реакция на вход и выход из состояния.
  • 39. Агент blinking_led (пояснения) class blinking_led final : public so_5::agent_t { state_t off{ this }, blinking{ this }, blink_on{ initial_substate_of{ blinking } }, blink_off{ substate_of{ blinking } }; public : struct turn_on_off : public so_5::signal_t {}; blinking_led( context_t ctx ) : so_5::agent_t{ std::move(ctx) } { this >>= off; off.just_switch_to< turn_on_off >( blinking ); blinking.just_switch_to< turn_on_off >( off ); blink_on .on_enter( []{ std::cout << "ON" << std::endl; } ) .on_exit( []{ std::cout << "off" << std::endl; } ) .time_limit( std::chrono::milliseconds{1500}, blink_off ); blink_off .time_limit( std::chrono::milliseconds{750}, blink_on ); } }; 39 time_limit задает ограничение на время пребывания агента в состоянии. По истечении этого времени агент автоматически меняет состояние.
  • 40. Еще один пример: request_processor (код) class request_processor : public so_5::agent_t { state_t st_waiting{ this }, st_working{ this }, ...; const so_5::mbox_t src_; public : request_processor(context_t ctx, so_5::mbox_t src) : so_5::agent_t(std::move(ctx)), src_(std::move(src)) {} virtual void so_define_agent() override { this >>= st_waiting; st_waiting .event(src_, &request_processor::on_request) .event(src_, &request_processor::on_status_when_waiting); st_working .event(src_, &request_processor::on_request) .event(src_, &request_processor::on_status_when_working); ... } ... private : void on_request(const request & cmd) {...} void on_status_when_waiting(mhood_t<get_status>) {...} void on_status_when_working(mhood_t<get_status>) {...} }; 40
  • 41. Еще один пример: request_processor (пояснения) class request_processor : public so_5::agent_t { state_t st_waiting{ this }, st_working{ this }, ...; const so_5::mbox_t src_; public : request_processor(context_t ctx, so_5::mbox_t src) : so_5::agent_t(std::move(ctx)), src_(std::move(src)) {} virtual void so_define_agent() override { this >>= st_waiting; st_waiting .event(src_, &request_processor::on_request) .event(src_, &request_processor::on_status_when_waiting); st_working .event(src_, &request_processor::on_request) .event(src_, &request_processor::on_status_when_working); ... } ... private : void on_request(const request & cmd) {...} void on_status_when_waiting(mhood_t<get_status>) {...} void on_status_when_working(mhood_t<get_status>) {...} }; 41 Почтовый ящик, из которого агент ожидает запросы. Создается кем-то и отдается агенту в качестве параметра конструктора.
  • 42. Еще один пример: request_processor (пояснения) class request_processor : public so_5::agent_t { state_t st_waiting{ this }, st_working{ this }, ...; const so_5::mbox_t src_; public : request_processor(context_t ctx, so_5::mbox_t src) : so_5::agent_t(std::move(ctx)), src_(std::move(src)) {} virtual void so_define_agent() override { this >>= st_waiting; st_waiting .event(src_, &request_processor::on_request) .event(src_, &request_processor::on_status_when_waiting); st_working .event(src_, &request_processor::on_request) .event(src_, &request_processor::on_status_when_working); ... } ... private : void on_request(const request & cmd) {...} void on_status_when_waiting(mhood_t<get_status>) {...} void on_status_when_working(mhood_t<get_status>) {...} }; 42 Метод so_define_agent() дает возможность агенту произвести "настройку" перед тем, как начать работать внутри SObjectizer. Обычно используется для создания подписок.
  • 43. Еще один пример: request_processor (пояснения) class request_processor : public so_5::agent_t { state_t st_waiting{ this }, st_working{ this }, ...; const so_5::mbox_t src_; public : request_processor(context_t ctx, so_5::mbox_t src) : so_5::agent_t(std::move(ctx)), src_(std::move(src)) {} virtual void so_define_agent() override { this >>= st_waiting; st_waiting .event(src_, &request_processor::on_request) .event(src_, &request_processor::on_status_when_waiting); st_working .event(src_, &request_processor::on_request) .event(src_, &request_processor::on_status_when_working); ... } ... private : void on_request(const request & cmd) {...} void on_status_when_waiting(mhood_t<get_status>) {...} void on_status_when_working(mhood_t<get_status>) {...} }; 43 Подписка на сообщения. Явным образом задается mbox из которого ожидаются сообщения. Тип сообщения не указан явно, он выводится автоматически из типа аргумента обработчика.
  • 44. Еще один пример: request_processor (пояснения) class request_processor : public so_5::agent_t { state_t st_waiting{ this }, st_working{ this }, ...; const so_5::mbox_t src_; public : request_processor(context_t ctx, so_5::mbox_t src) : so_5::agent_t(std::move(ctx)), src_(std::move(src)) {} virtual void so_define_agent() override { this >>= st_waiting; st_waiting .event(src_, &request_processor::on_request) .event(src_, &request_processor::on_status_when_waiting); st_working .event(src_, &request_processor::on_request) .event(src_, &request_processor::on_status_when_working); ... } ... private : void on_request(const request & cmd) {...} void on_status_when_waiting(mhood_t<get_status>) {...} void on_status_when_working(mhood_t<get_status>) {...} }; 44 Обработчик для сообщений с типом request. Такой формат обработчика требует, чтобы request был сообщением, а не сигналом.
  • 45. Еще один пример: request_processor (пояснения) class request_processor : public so_5::agent_t { state_t st_waiting{ this }, st_working{ this }, ...; const so_5::mbox_t src_; public : request_processor(context_t ctx, so_5::mbox_t src) : so_5::agent_t(std::move(ctx)), src_(std::move(src)) {} virtual void so_define_agent() override { this >>= st_waiting; st_waiting .event(src_, &request_processor::on_request) .event(src_, &request_processor::on_status_when_waiting); st_working .event(src_, &request_processor::on_request) .event(src_, &request_processor::on_status_when_working); ... } ... private : void on_request(const request & cmd) {...} void on_status_when_waiting(mhood_t<get_status>) {...} void on_status_when_working(mhood_t<get_status>) {...} }; 45 Обработчики для сообщений или сигналов с типом get_status. Такой формат обработчика позволяет обрабатывать и сообщения, и сигналы. Что удобно при написании обобщенного кода в агентах.
  • 46. Кооперации агентов Кооперации агентов решают задачу одномоментной регистрации в SObjectizer группы взаимосвязанных агентов. 46
  • 47. Если бы были супервизоры, то... 47
  • 48. Супервизоров нет, есть кооперации 48
  • 49. Наполнение и регистрация коопераций so_5::environment_t & env = ...; // Доступ к SObjectizer. // Заставляем SObjectizer создать объект кооперации. auto coop = env.create_coop( "data_acquire_demo" ); // Наполняем кооперацию прикладными агентами. Заодно привязываем агентов к разным диспетчерам для того, // чтобы агенты работали на разных рабочих нитях. coop->make_agent_with_binder<device_reader>( so_5::disp::one_thread::create_private_disp(env, "device")->binder(), ... ); coop->make_agent<data_processor>(...); coop->make_agent_with_binder<db_writer>( so_5::disp::one_thread::create_private_disp(env, "db")->binder(), ... ); // Осталось только зарегистрировать кооперацию. env.register_coop(std::move(coop)); 49 (1)
  • 50. Наполнение и регистрация коопераций so_5::environment_t & env = ...; // Доступ к SObjectizer. // Заставляем SObjectizer создать объект кооперации. auto coop = env.create_coop( "data_acquire_demo" ); // Наполняем кооперацию прикладными агентами. Заодно привязываем агентов к разным диспетчерам для того, // чтобы агенты работали на разных рабочих нитях. coop->make_agent_with_binder<device_reader>( so_5::disp::one_thread::create_private_disp(env, "device")->binder(), ... ); coop->make_agent<data_processor>(...); coop->make_agent_with_binder<db_writer>( so_5::disp::one_thread::create_private_disp(env, "db")->binder(), ... ); // Осталось только зарегистрировать кооперацию. env.register_coop(std::move(coop)); 50 (2)
  • 51. Наполнение и регистрация коопераций so_5::environment_t & env = ...; // Доступ к SObjectizer. // Заставляем SObjectizer создать объект кооперации. auto coop = env.create_coop( "data_acquire_demo" ); // Наполняем кооперацию прикладными агентами. Заодно привязываем агентов к разным диспетчерам для того, // чтобы агенты работали на разных рабочих нитях. coop->make_agent_with_binder<device_reader>( so_5::disp::one_thread::create_private_disp(env, "device")->binder(), ... ); coop->make_agent<data_processor>(...); coop->make_agent_with_binder<db_writer>( so_5::disp::one_thread::create_private_disp(env, "db")->binder(), ... ); // Осталось только зарегистрировать кооперацию. env.register_coop(std::move(coop)); 51 (3)
  • 52. Регистрация кооперации – это просто? 1. Проверка уникальности имени. 2. Проверка родительской кооперации (если есть). 3. Запрос ресурсов у диспетчеров. 4. Вызов so_define_agent() для каждого агента. 5. Окончательная привязка агентов к диспетчерам, инициация so_evt_start(). 52
  • 53. По традиции: hello_world #include <so_5/all.hpp> class hello_world final : public so_5::agent_t { public : using so_5::agent_t::agent_t; virtual void so_evt_start() override { std::cout << "Hello, World!" << std::endl; so_deregister_agent_coop_normally(); } }; int main() { so_5::launch([](so_5::environment_t & env) { env.register_agent_as_coop("hello", env.make_agent<hello_world>()); }); return 0; } 53
  • 54. Распределенности в SObjectizer-5 нет Распределенность "из коробки" была в SObjectizer-4. Но: ● для разных задач нужны разные протоколы (одно дело – передача телеметрии, другое – передача больших BLOB-ов); ● back pressure в асинхронном обмене сообщении сам по себе непрост. В случае IPC эта проблема усугубляется; ● интероперабельность с другими языками программирования. Точнее, ее отсутствие. Поэтому в SObjectizer-5 распределенности нет. 54
  • 55. Куда же мы идем? 55
  • 56. Реализация Модели Акторов не самоцель У нас нет цели сделать из SObjectizer самую лучшую и/или самую полноценную реализацию Модели Акторов. Мы говорим "акторный фреймворк" только потому, что: ● так проще объяснять, что это в принципе такое; ● маркетинг. 56
  • 57. Publish/Subscribe Поддержка Publish/Subscribe была с самого начала. Более того, в SObjectizer-5 понятие direct mbox-а появилось не сразу, изначально были только MPMC-mbox-ы. 57
  • 58. CSP (Communicating Sequential Processes) В декабре 2015-го добавлены message chains. struct ping {}; struct pong {}; auto ch1 = so_5::create_mchain(env); auto ch2 = so_5::create_mchain(env); std::thread pinger{ [ch1, ch2]{ while(true) { so_5::send<ping>(ch2); so_5::receive(ch1, so_5::infinite_wait, [](pong){}); } } }; std::thread ponger{ [ch1, ch2]{ while(true) { so_5::receive(ch2, so_5::infinite_wait, [&ch1](ping) { so_5::send<pong>(ch1); } ); } } }; 58
  • 59. CSP (Communicating Sequential Processes) В декабре 2015-го добавлены message chains. struct ping {}; struct pong {}; auto ch1 = so_5::create_mchain(env); auto ch2 = so_5::create_mchain(env); std::thread pinger{ [ch1, ch2]{ while(true) { so_5::send<ping>(ch2); so_5::receive(ch1, so_5::infinite_wait, [](pong){}); } } }; std::thread ponger{ [ch1, ch2]{ while(true) { so_5::receive(ch2, so_5::infinite_wait, [&ch1](ping) { so_5::send<pong>(ch1); } ); } } }; 59
  • 60. Поэтому на самом-то деле... It's all about in-process message dispatching! 60
  • 61. Наша же цель в том, чтобы... ...предоставить разработчику небольшой качественный и стабильный инструмент. Практичный и настраиваемый под нужды пользователя. Стабильность и совместимость. За это мы готовы платить. Например, поддержка Ubuntu 14.04 LTS и тамошнего gcc-4.8 для нас важнее, чем возможность использовать C++14 в коде SObjectizer. 61
  • 63. В двух словах Современный C++ для нас очень важен. Даже так: если бы не C++11, SObjectizer-5 вряд ли появился бы. Есть в C++11 вещи, без которых SObjectizer сейчас сложно представить... 63
  • 64. Variadic templates и perfect forwarding Используются в SObjectizer повсеместно: template<typename MSG, typename... ARGS> void send(const mbox_t & to, ARGS &&... args) { auto m = make_unique<MSG>(std::forward<ARGS>(args)...); to->deliver_message(std::move(m)); } class agent_coop_t { public : ... template< class AGENT, typename... ARGS > AGENT * make_agent( ARGS &&... args ) { auto a = make_unique< AGENT >( environment(), std::forward<ARGS>(args)... ); return this->add_agent( std::move( a ) ); } }; 64
  • 65. Лямбды (особенно в сочетании с шаблонами) Ну очень сильно помогают. В том числе и для обеспечения гарантий безопасности исключений... void agent_core_t::next_coop_reg_step__update_registered_coop_map( const coop_ref_t & coop_ref, coop_t * parent_coop_ptr ) { m_registered_coop[ coop_ref->query_coop_name() ] = coop_ref; m_total_agent_count += coop_ref->query_agent_count(); so_5::details::do_with_rollback_on_exception( [&] { next_coop_reg_step__parent_child_relation( coop_ref, parent_coop_ptr ); }, [&] { m_total_agent_count -= coop_ref->query_agent_count(); m_registered_coop.erase( coop_ref->query_coop_name() ); } ); } 65 (1) (2) (3)
  • 66. auto и decltype Сложно переоценить важность auto в современном C++. Особенно для вывода типа результата функции. template< typename MAIN_ACTION, typename ROLLBACK_ACTION > auto do_with_rollback_on_exception( MAIN_ACTION main_action, ROLLBACK_ACTION rollback_action ) -> decltype(main_action()) { using result_type = decltype(main_action()); using namespace rollback_on_exception_details; rollbacker_t< ROLLBACK_ACTION > rollbacker{ rollback_action }; return executor< result_type, MAIN_ACTION, ROLLBACK_ACTION >::exec( main_action, rollbacker ); } 66
  • 67. Стандартная библиотека C++11 Появление thread, mutex, condition_variable, atomic, unordered_map и пр. в стандартной библиотеке C++11 позволило нам избавиться от такой зависимости, как ACE. Стало гораздо легче. Правда, пришлось делать свою реализацию таймеров, но это уже совсем другая история... 67
  • 68. Краткое резюме по современному C++ C++11/14 – это уже совсем другой C++. Использовать современный C++ намного удобнее, особенно, если есть возможность пользоваться C++14. Инфраструктура вокруг языка продолжает желать много лучшего. Но мы над этим работаем ;) 68
  • 70. SObjectizer: https://sourceforge.net/p/sobjectizer или https://github.com/eao197/so-5-5 Документация по SObjectizer: https://sourceforge.net/p/sobjectizer/wiki/Home/ Серия статей о SObjectizer на русском: SObjectizer: что это, для чего это и почему это выглядит именно так? От простого к сложному: Часть I, Часть II, Часть III. Акторы в виде конечных автоматов – это плохо или хорошо? Проблема перегрузки агентов и средства борьбы с ней. Нежная дружба агентов и исключений. Серия презентаций о SObjectizer на английском "Dive into SObjectizer-5.5": Intro, Agent's States, More About Coops, Exceptions, Timers, Synchonous Interaction, Message Limits, Dispatchers, Message Chains. 70