73
CoreHard C++ Autumn 2016 Модель Акторов и C++: что, зачем и как? Евгений Охотников

Модель акторов и C++ что, зачем и как?

Embed Size (px)

Citation preview

Page 1: Модель акторов и C++ что, зачем и как?

CoreHard C++ Autumn 2016

Модель Акторов и C++:что, зачем и как?

Евгений Охотников

Page 2: Модель акторов и C++ что, зачем и как?

О чем пойдет речь?

Многопоточность и parallel concurrent computing.

Модель Акторов в двух-трех-четырех-...-двенадцати слайдах.

Сегодняшние иконы "Модели Акторов". Все две штуки.

А как же C++ (пока его еще не закопали)?

NIH*-синдром или что есть готового для C++?

2* NIH - Not Invented Here

Page 3: Модель акторов и C++ что, зачем и как?

Многопоточность – это...инструмент, который активно применяется в двух очень разных областях:

● parallel computing (одновременно выполняются одинаковые операции над разными наборами однотипных данных);

● concurrent computing (одновременно выполняются совсем разные операции, возможно, над совершенно разными данными).

Concurrent computing может быть и без многопоточности. Но на базе многопоточности concurrent computing доставляет больше всего...

3

Page 4: Модель акторов и C++ что, зачем и как?

Далее только о многопоточности......применительно к concurrent computing.

Именно здесь Модель Акторов оказывается очень удобной.

А для parallel computing используются другие подходы и другие инструменты...

4

Page 5: Модель акторов и C++ что, зачем и как?

Итак, многопоточность – это сложноТаки да, это сложно.

Даже имея 20 лет опыта.

Но почему?

5

Page 6: Модель акторов и C++ что, зачем и как?

Изменяемое разделяемое состояниеИменно изменяемое разделяемое состояние является одним из главных факторов сложности в многопоточном программировании.

6

Page 7: Модель акторов и C++ что, зачем и как?

Как упростить себе жизнь?

7

Page 8: Модель акторов и C++ что, зачем и как?

Убрать разделяемое состояниеНичего не разделяем. Ни за что не боремся.

Пусть каждый поток владеет своими собственными данными. И никто кроме потока-владельца не имеет к этим данным доступа.

Принцип shared nothing* (широко известен в узких кругах).

8* https://en.wikipedia.org/wiki/Shared_nothing_architecture

Page 9: Модель акторов и C++ что, зачем и как?

Убрать разделяемое состояниеНо что делать, если потоку X потребовалась какая-то информация, которая есть у потока Y?

Что делать, если поток Y хочет, чтобы поток Z обновил какие-то данные у себя?

9

Page 10: Модель акторов и C++ что, зачем и как?

Потокам нужно общаться, но как?Кажется, что есть два способа:

1. Синхронно.2. Асинхронно.

Но это только кажется...

10

Page 11: Модель акторов и C++ что, зачем и как?

На самом-то деле......никакой синхронности, только хардкор асинхронное взаимодействие.

Например, на основе отсылки и приема сообщений:

● поток X отсылает сообщение-запрос потоку Y;● поток Y когда-то получит запрос потока X, обработает запрос и отошлет

потоку X ответ;● поток Y отсылает сообщение-обновление потоку Z;● поток Z когда-то получит сообщение-обновление от потока Y и обновит

те данные, которые принадлежат потоку Z.

11

Page 12: Модель акторов и C++ что, зачем и как?

Просто же!Есть потоки. У каждого есть очередь входящих сообщений.

Поток спит, если очередь входящих сообщений пуста.

Поток просыпается, когда для него появляются входящие сообщения.

Если потоку X нужно что-то от потока Y, то X отсылает сообщение в очередь входящих сообщений потока Y.

Если поток Y хочет ответить потоку X, то Y отсылает сообщение в очередь входящих сообщений потока X.

12

Page 13: Модель акторов и C++ что, зачем и как?

А если сообщения несут копию данных......то мы получаем очень приятный бонус: прозрачный переход к распределенности.

Если поток Y передал в сообщении Msg копию данных, а не ссылку на них, то уже не суть важно, пойдет ли сообщение Msg в локальную очередь входящих сообщений потока Z. Или же в очередь исходящих данных для передачи на другой хост.

Сообщение самодостаточно. Поэтому может быть сериализовано, передано по сети, десериализовано и обработано.

13

Page 14: Модель акторов и C++ что, зачем и как?

Вот и весь фокус :)Модель акторов именно про это.

Про изолированные потоки управления.

И про их взаимодействие на основе обмена сообщениями.

14

Page 15: Модель акторов и C++ что, зачем и как?

Модель АкторовПоявилась в 1973-ем году благодаря работам Карла Хьюитта (Carl Hewitt).

Была затем развита в 1981-ом Уильямом Клингером (William Clinger).

И в 1985-ом Гулом Агха (Gul Agha).

Это если говорить про формализацию Модели Акторов.

Неформально она открывалась и переоткрывалась множество раз.

15

Page 16: Модель акторов и C++ что, зачем и как?

Модель АкторовОтправные точки для желающих погрузиться в формальную теорию Модели Акторов:

https://en.wikipedia.org/wiki/History_of_the_Actor_model

https://en.wikipedia.org/wiki/Actor_model

https://en.wikipedia.org/wiki/Actor_model_theory

16

Page 17: Модель акторов и C++ что, зачем и как?

Модель Акторов. Основные принципы● актор – это некая сущность, обладающая поведением;● акторы реагируют на входящие сообщения;● получив сообщение актор может:

○ отослать некоторое (конечное) количество сообщений другим акторам;

○ создать некоторое (конечное) количество новых акторов;○ определить для себя новое поведение для обработки последующих

сообщений.

17

Page 18: Модель акторов и C++ что, зачем и как?

Актор – это некоторая сущностьНе более того.

Актором может быть отдельный процесс. Например, Erlang.

Актором может быть отдельный поток (OS thread, "green" thread, fiber, ...). Например, goroutine в Go может рассматриваться как актор.

Актором может быть объект, которому кто-то выделяет рабочий контекст. Например, Akka.

18

Page 19: Модель акторов и C++ что, зачем и как?

Еще раз: актор – это некоторая сущностьМодель Акторов не требует, чтобы актор был процессом или потоком, или конечным автоматом, или чем-то еще.

Поэтому существующие и востребованные на практике реализации Модели Акторов очень сильно отличаются друг от друга.

19

Page 20: Модель акторов и C++ что, зачем и как?

Модели Акторов уже более 40 летЗа это время Модель Акторов пережила несколько волн популярности и забвения*

Сейчас очередная волна популярности. Она началась где-то около 10-12 лет назад. Основным двигателем интереса стали Erlang, а затем Akka.

20* Why has the actor model not succeeded?

Page 21: Модель акторов и C++ что, зачем и как?

Современные иконы: ErlangПожалуй, самая известная реализация – язык Erlang*

http://www.erlang.org/

Не только сам язык, но и Erlang VM, и OTP, и вообще все, что понастроили вокруг.

21* Я не встречал, чтобы Джо Армстронг говорил, что на Erlang повлияла Модель Акторов

Page 22: Модель акторов и C++ что, зачем и как?

Современные иконы: ErlangЗародился в одной из исследовательских лабораторий Ericsson-а в 1986-ом году благодаря Джо Армстронгу (Joe Armstrong).

В 1995-ом году после закрытия неудачного проекта AXE-N (на C++, кстати) был выбран в качестве основного языка для проекта AXD.

Результатом стал успешный программно-аппаратный продукт AXD301 в котором было более миллиона строк на Erlang.

22

Page 23: Модель акторов и C++ что, зачем и как?

Современные иконы: ErlangВ конце 1990-х использование Erlang-а для новых проектов в Ericsson Radio AB было запрещено.

Джо Армстронг покинул Ericsson.

Erlang ушел в OpenSource.

Erlang доказал свою состоятельность вне Ericsson-а. Ericsson одумался.

В 2004-ом году Джо Армстронг возвратился в Ericsson.

23

Page 24: Модель акторов и C++ что, зачем и как?

Современные иконы: ErlangЗа последние годы Erlang многократно показывал себя с лучшей стороны в очень серьезных проектах (например, WhatsApp использует Erlang).

Многие компании убедившись в достоинствах Erlang-а задействуют Erlang в своих разработках.

Из близких нам примеров: Wargaming.

24

Page 25: Модель акторов и C++ что, зачем и как?

Современные иконы: Erlang-module(tut15).-export([start/0, ping/2, pong/0]).

ping(0, Pong_PID) -> Pong_PID ! finished, io:format("ping finished~n", []);

ping(N, Pong_PID) -> Pong_PID ! {ping, self()}, receive pong -> io:format("Ping received pong~n", []) end, ping(N - 1, Pong_PID).

25

pong() -> receive finished -> io:format("Pong finished~n", []); {ping, Ping_PID} -> io:format("Pong received ping~n", []), Ping_PID ! pong, pong() end.

start() -> Pong_PID = spawn(tut15, pong, []), spawn(tut15, ping, [3, Pong_PID]).

Page 26: Модель акторов и C++ что, зачем и как?

Современные иконы: AkkaФреймворк для Java и Scala.

http://akka.io/

История началась в 2006: Филипп Холлер (Philipp Haller) разработал реализацию Модели Акторов для стандартной библиотеки языка Scala.

В 2008-ом Джонас Бонер (Jonas Bonér) начал делать Erlang-овский OTP для Scala на базе акторов из Scala-stdlib*. Первая публичная версия Akka вышла в 2010-ом.

26* http://www.lightbend.com/akka-five-year-anniversary

Page 27: Модель акторов и C++ что, зачем и как?

Современные иконы: AkkaОчень популярна в JVM-мире.

Соответственно, находит широкое применение там, где позиции JVM традиционно сильны: Web, онлайн-сервисы и суровый энтерпрайз...

Яркие примеры: LinkedIn и Twitter.

Стоящие за Akka люди так же причастны к таким современным buzz-word-ам, как Reactive Manifesto* и Microservices** :)

27

* http://www.reactivemanifesto.org/** https://en.wikipedia.org/wiki/Microservices

Page 28: Модель акторов и C++ что, зачем и как?

Современные иконы: Akkaimport akka.actor._

case object PingMessagecase object PongMessagecase object StartMessagecase object StopMessage

class Ping(pong: ActorRef) extends Actor { var count = 0 def incrementAndPrint { count += 1; println("ping") } def receive = { case StartMessage => incrementAndPrint pong ! PingMessage case PongMessage => incrementAndPrint if (count > 99) { sender ! StopMessage println("ping stopped") context.stop(self) } else { sender ! PingMessage }

28

}}

class Pong extends Actor { def receive = { case PingMessage => println(" pong") sender ! PongMessage case StopMessage => println("pong stopped") context.stop(self) }}

object PingPongTest extends App { val system = ActorSystem("PingPongSystem") val pong = system.actorOf(Props[Pong], name = "pong") val ping = system.actorOf(Props(new Ping(pong)), name = "ping") // start them going ping ! StartMessage}

http://alvinalexander.com/scala/scala-akka-actors-ping-pong-simple-example

Page 29: Модель акторов и C++ что, зачем и как?

В чем сила, брат?Что же такое предлагают своим пользователям Erlang и Akka?

Почему они востребованы и используются в очень серьезных проектах?

29

Page 30: Модель акторов и C++ что, зачем и как?

Вот в чем:1. Простота. Отсутствие разделяемых данных и взаимодействие

посредством асинхронных сообщений спасают от ужасов программирования на мьютексах.

2. Масштабирование. Акторов может быть хоть миллион. Даже самую мелкую задачку можно поручить отдельному актору. Акторов можно распределять по нескольким процессам и нескольким нодам, при асинхронном обмене сообщениями это не суть важно.

3. Отказоустойчивость. Какие-то акторы могут "падать", другие акторы это обнаружат и исправят (см. систему супервизоров Erlang-а*). Shared nothing + message-passing сильно помогают и тут.

30* http://erlang.org/doc/man/supervisor.html

Page 31: Модель акторов и C++ что, зачем и как?

Так говорил Джо Армстронг

I also suspect that the advent of true parallel CPU cores will make programming parallel systems using conventional mutexes and shared data structures almost impossibly difficult, and that the pure message-passing systems will become the dominant way to program parallel systems.

Так же я подозреваю, что пришествие настоящих многоядерных CPU сделает программирование параллельных систем с использованием традиционных мьютексов и разделяемых структур данных сложным до невозможности, и что именно обмен сообщениями станет доминирующим способом разработки параллельных систем.

Disclaimer: перевод приблизительный, но основной смысл передан верно.

31A History of Erlang, Joe Armstrong, 2007.

Page 32: Модель акторов и C++ что, зачем и как?

Кстати об отказоустойчивостиErlang и Java/Scala (речь об Akka) – это безопасные языки, работающие в управляемых средах (Erlang VM и JVM).

На отказоустойчивость это влияет самым непосредственным образом.

Деление на ноль в одном из процессов внутри Erlang VM и деление на ноль в одном из потоков большого приложения на C++ – это две большие разницы.

32

Page 33: Модель акторов и C++ что, зачем и как?

А что с C++?Есть ли смысл в использовании Модели Акторов в C++?

33

Page 34: Модель акторов и C++ что, зачем и как?

Неверный вопрос!Начинать нужно с более важного вопроса:

А нужен ли вообще C++?

34

Page 35: Модель акторов и C++ что, зачем и как?

Серьезно? И это на CoreHard C++?

Таки да :(

35

Page 36: Модель акторов и C++ что, зачем и как?

Так нужен ли вообще C++?С++ старый язык (более 30 лет с публичного релиза).

За это время стал настоящим монстром, стандарт которого насчитывает ~1500 страниц. И это еще C++17 не приняли...

Вобрал в себя множество нового (в переводе на русский: стал еще сложнее).

Сохранил при этом совместимость с еще более древним языком С. Так что с отстрелом конечностей все в порядке.

Каждый большой проект использует собственное подмножество C++.

Огромное количество копролитов мамонта легаси кода.36

Page 37: Модель акторов и C++ что, зачем и как?

Тем не менее......какой еще мейнстримовый нативный язык без GC:

● настолько же быстр и эффективен?● позволяет работать на самом низком уровне?● позволяет подниматься на высокие уровни абстракции (ООП,

обобщенное программирование, метапрограммирование)?● снабжен таким же количеством разнообразного инструментария?● так же хорошо и досконально описан в огромном количестве книг,

статей, руководств, рекомендаций и пр.?● обладает таким же большим коммьюнити?

Если объективно посмотреть по сторонам, то альтернатив не так уж и много.37

Page 38: Модель акторов и C++ что, зачем и как?

Вывод прост: нужен, однозначно!А раз нужен, то нужны и инструменты, облегчающие жизнь C++ разработчикам.

В том числе и реализации Модели Акторов для C++.

38

Page 39: Модель акторов и C++ что, зачем и как?

Больше фреймворков, хороших и разных!Более-менее актуальный список инструментов для C++ можно найти здесь:

https://en.wikipedia.org/wiki/Actor_model#Actor_libraries_and_frameworks

Там не все. А часть того, что есть, уже умерло. Но все-таки.

39

Page 40: Модель акторов и C++ что, зачем и как?

Четверо смелых для беглого знакомстваДалее быстрый забег "по верхам" для знакомства с четырьмя C++ фреймворками, которые:

● на C++;● подают признаки жизни (что уже не мало, на самом-то деле);● кросс-платформенны;● интересны с той или иной стороны.

Есть еще, например, OOSMOS* и Asyncronous Agents Library** от MS, но они не вошли в обзор из-за нарушения каких-то из перечисленных выше пунктов.

40

* http://www.oosmos.com/** https://msdn.microsoft.com/en-us/library/dd492627.aspx

Page 41: Модель акторов и C++ что, зачем и как?

QP/C++http://www.state-machine.com/qpcpp/

C++98/03. Предназначен для разработки встраиваемого ПО. В том числе и для работы на голом железе.

Двойная лицензия.

Более 15 лет развития и эксплуатации.

Декларируется приличный уровень соответствия MISRA C++2008.

41

Page 42: Модель акторов и C++ что, зачем и как?

QP/C++Акторы в QP/C++ – это иерархические конечные автоматы. Называются активными объектами.

Код активных объектов можно писать обычным образом. Т.е. вручную набирать код C++ классов, методов и вот это вот все.

А можно "нарисовать" активный объект в специальном визуальном инструменте для проектирования и C++ код будет сгенерирован.

Контекст для работы активных объектов предоставляет QP. В зависимости от окружения активные объекты могут работать на отдельных нитях. А могут и сообща на одной-единственной.

42

Page 43: Модель акторов и C++ что, зачем и как?

QP/C++ (пример кода: blinky.h)#ifndef blinky_h#define blinky_h

using namespace QP;

enum BlinkySignals { DUMMY_SIG = Q_USER_SIG, MAX_PUB_SIG, // the last published signal

TIMEOUT_SIG, MAX_SIG // the last signal};

extern QMActive * const AO_Blinky; // opaque pointer

#endif // blinky_h

43

Page 44: Модель акторов и C++ что, зачем и как?

QP/C++ (пример кода: main.cpp)#include "qpcpp.h"#include "bsp.h"#include "blinky.h"

int main() { static QEvt const *blinkyQSto[10]; // Event queue storage for Blinky

BSP_init(); // initialize the Board Support Package QF::init(); // initialize the framework and the underlying RT kernel

// instantiate and start the active objects... AO_Blinky->start(1U, // priority blinkyQSto, Q_DIM(blinkyQSto), // event queue (void *)0, 0U); // stack (unused)

return QF::run(); // run the QF application}

44

Page 45: Модель акторов и C++ что, зачем и как?

QP/C++ (пример кода: blinky.cpp, 1)#include "qpcpp.h"#include "bsp.h"#include "blinky.h"

class Blinky : public QActive {private: QTimeEvt m_timeEvt;

public: Blinky();

protected: static QState initial(Blinky * const me, QEvt const * const e); static QState off(Blinky * const me, QEvt const * const e); static QState on(Blinky * const me, QEvt const * const e);};

Blinky l_blinky;

QMActive * const AO_Blinky = &l_blinky; // opaque pointer

45

Page 46: Модель акторов и C++ что, зачем и как?

QP/C++ (пример кода: blinky.cpp, 2)Blinky::Blinky() : QActive(Q_STATE_CAST(&Blinky::initial)), m_timeEvt(this, TIMEOUT_SIG, 0U){}

QState Blinky::initial(Blinky * const me, QEvt const * const e) { (void)e; // unused parameter

// arm the time event to expire in half a second and every half second me->m_timeEvt.armX(BSP_TICKS_PER_SEC/2U, BSP_TICKS_PER_SEC/2U); return Q_TRAN(&Blinky::off);}

46

Page 47: Модель акторов и C++ что, зачем и как?

QP/C++ (пример кода: blinky.cpp, 3)QState Blinky::off(Blinky * const me, QEvt const * const e){ QState status; switch (e->sig) { case Q_ENTRY_SIG: { BSP_ledOff(); status = Q_HANDLED(); break; } case TIMEOUT_SIG: { status = Q_TRAN(&Blinky::on); break; } default: { status = Q_SUPER(&QHsm::top); break; } } return status;}

47

QState Blinky::on(Blinky * const me, QEvt const * const e){ QState status; switch (e->sig) { case Q_ENTRY_SIG: { BSP_ledOn(); status = Q_HANDLED(); break; } case TIMEOUT_SIG: { status = Q_TRAN(&Blinky::off); break; } default: { status = Q_SUPER(&QHsm::top); break; } } return status;}

Page 48: Модель акторов и C++ что, зачем и как?

Just::Thread Pro: Actors Editionhttp://www.stdthread.co.uk/pro/

C++11.

Платная библиотека.

Автор – Энтони Уильямс (Anthony Williams). Он же написал книгу "C++ Concurrency in Action".

На этом, пожалуй, достоинства заканчиваются :)

Под каждого актора выделяется отдельный поток ОС.

48

Page 49: Модель акторов и C++ что, зачем и как?

Just::Thread Pro: Actors Edition (ping-pong)#include <jss/actor.hpp>#include <iostream>#include <thread>

int main(){ struct pingpong { jss::actor_ref sender;

pingpong(jss::actor_ref sender_): sender(sender_) {} }; jss::actor pp1( []{ for(;;) { jss::actor::receive().match<pingpong>( [](pingpong p){ std::cout<<"ping\n"; p.sender.send(pingpong(jss::actor::self())); }); } });

49

jss::actor pp2( []{ for(;;) { jss::actor::receive().match<pingpong>( [](pingpong p){ std::cout<<"pong\n"; p.sender.send(pingpong(jss::actor::self())); }); } });

pp1.send(pingpong(pp2));

std::this_thread::sleep_for(std::chrono::seconds(2)); pp1.stop(); pp2.stop();}

Page 50: Модель акторов и C++ что, зачем и как?

C++ Actor Framework (он же CAF)http://www.actor-framework.org/

C++11 и выше (чем выше, тем лучше).

OpenSource под BSD-3-CLAUSE лицензией.

Самая распиаренная реализация Модели Акторов для C++.

Позиционирует себя как очень быстрый фреймворк. Не то, чтобы так уж заслуженно* ;)

Пока еще не стабилизировался.

50* Performance Comparison SO-5.5.15.2 vs CAF-0.14.4

Page 51: Модель акторов и C++ что, зачем и как?

C++ Actor Framework (он же CAF)Изначально копировал Erlang в C++ настолько близко, насколько это возможно. Постепенно видоизменяется и приобретает свои уникальные черты.

Ценой за мимикрию под Erlang являются высокие требования CAF-а к уровню поддержки стандартов в C++ в компиляторе.

Официальная поддержка Windows/VC++ появилась только после выхода Visual Studio 2015 update 3.

Есть поддержка распределенности. Собственный протокол с реализацией на базе Boost::Asio.

51

Page 52: Модель акторов и C++ что, зачем и как?

C++ Actor Framework (fixed_stack, 1)#include <cassert>#include <cstdint>#include <iostream>#include "caf/all.hpp"

using std::endl;using namespace caf;

namespace {

using pop_atom = atom_constant<atom("pop")>;using push_atom = atom_constant<atom("push")>;

enum class fixed_stack_errc : uint8_t { push_to_full = 1, pop_from_empty };

error make_error(fixed_stack_errc x) { return error{static_cast<uint8_t>(x), atom("FixedStack")};}

52

Page 53: Модель акторов и C++ что, зачем и как?

C++ Actor Framework (fixed_stack, 2)class fixed_stack : public event_based_actor {public: fixed_stack(actor_config& cfg, size_t stack_size) : event_based_actor(cfg), size_(stack_size) { full_.assign( [=](push_atom, int) -> error { return fixed_stack_errc::push_to_full; }, [=](pop_atom) -> int { auto result = data_.back(); data_.pop_back(); become(filled_); return result; } );

53

Page 54: Модель акторов и C++ что, зачем и как?

C++ Actor Framework (fixed_stack, 3) filled_.assign( [=](push_atom, int what) { data_.push_back(what); if (data_.size() == size_) become(full_); }, [=](pop_atom) -> int { auto result = data_.back(); data_.pop_back(); if (data_.empty()) become(empty_); return result; } );

54

Page 55: Модель акторов и C++ что, зачем и как?

C++ Actor Framework (fixed_stack, 4) empty_.assign( [=](push_atom, int what) { data_.push_back(what); become(filled_); }, [=](pop_atom) -> error { return fixed_stack_errc::pop_from_empty; } ); }

55

Page 56: Модель акторов и C++ что, зачем и как?

C++ Actor Framework (fixed_stack, 5) behavior make_behavior() override { assert(size_ < 2); return empty_; }

private: size_t size_; std::vector<int> data_; behavior full_; behavior filled_; behavior empty_;};

56

Page 57: Модель акторов и C++ что, зачем и как?

C++ Actor Framework (fixed_stack, 6)void caf_main(actor_system& system) { scoped_actor self{system}; auto st = self->spawn<fixed_stack>(5u); // fill stack for (int i = 0; i < 10; ++i) self->send(st, push_atom::value, i); // drain stack aout(self) << "stack: { "; bool stack_empty = false; while (!stack_empty) { self->request(st, std::chrono::seconds(10), pop_atom::value).receive( [&](int x) { aout(self) << x << " "; }, [&](const error&) { stack_empty = true; } ); } aout(self) << "}" << endl; self->send_exit(st, exit_reason::user_shutdown);}} // namespace <anonymous>CAF_MAIN()

57

Page 58: Модель акторов и C++ что, зачем и как?

SObjectizer-5https://sourceforge.net/projects/sobjectizer/ или https://github.com/eao197/so-5-5

C++11 (минимальные требования к компилятору: GCC 4.8, MSVC++12.0).

OpenSource под BSD-3-CLAUSE лицензией.

С очень долгой историей:

1995-2000: Гомель, КБ Системного Программирования, проект SCADA Objectizer;2002-...: Гомель-Москва, Интервэйл, проект SObjectizer-4;2010-...: Гомель-Москва, Интервэйл, The SObjectizer Team, проект SObjectizer-5.

58

Page 59: Модель акторов и C++ что, зачем и как?

SObjectizer-5SO-4 в продакшене c 2002-го года. И до сих пор работает.

SO-5 в продакшене с 2011-го года.

Обратная совместимость – must have.Без фанатизма, но позволить себе вносить ломающие совместимость изменения в каждый релиз мы не можем. От слова совсем.

Версия SO-5.5.0 вышла в октябре 2014-го. С тех пор в ветке 5.5.* нет ломающих изменений. Последняя стабильная версия 5.5.18 – сентябрь 2016.

59

Page 60: Модель акторов и C++ что, зачем и как?

SObjectizer-5Акторы в SO-5 называются агентами. Так сложилось.

Агенты в SO-5 – это иерархические конечные автоматы (вложенные состояния, обработчики входа-выхода, история, временные лимиты).

Рабочий контекст агентам предоставляют диспетчеры.

Разработчику "из коробки" доступно восемь типов готовых диспетчеров.

Распределенность в SO-5 не поддерживается. Был опыт в SO-4. Поэтому в SO-5 решили обходиться готовыми сторонними инструментами по задачу (MQTT, AMQP, HTTP и т.д.).

60

Page 61: Модель акторов и C++ что, зачем и как?

SObjectizer-5Главное отличие от прочих фреймворков: SO-5 – это симбиоз Модели Акторов, модели Publish/Subscribe. С элементами CSP*

Сообщения отсылаются не агенту, а в message box (mbox). За mbox-ом может быть один агент, может быть множество агентов. Или ни одного.

Mbox является аналогом Topic-а из Pub/Sub. Отсылка сообщения – аналогом Publish-а из Pub/Sub. И, как и в Pub/Sub, агент должен подписаться на сообщение, чтобы получить его.

61* https://en.wikipedia.org/wiki/Communicating_sequential_processes

Page 62: Модель акторов и C++ что, зачем и как?

SObjectizer-5 (blinking_led, 1)#include <iostream>

#include <so_5/all.hpp>

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 {};

62

Page 63: Модель акторов и C++ что, зачем и как?

SObjectizer-5 (blinking_led, 2) blinking_led( context_t ctx ) : so_5::agent_t{ 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{1250}, blink_off );

blink_off .time_limit( std::chrono::milliseconds{750}, blink_on ); }};

63

Page 64: Модель акторов и C++ что, зачем и как?

SObjectizer-5 (blinking_led, 3)int main() { so_5::launch( []( so_5::environment_t & env ) { so_5::mbox_t m; env.introduce_coop( [&]( so_5::coop_t & coop ) { auto led = coop.make_agent< blinking_led >(); m = led->so_direct_mbox(); } );

auto pause = []( unsigned int v ) { std::this_thread::sleep_for( std::chrono::seconds{v} ); };

so_5::send< blinking_led::turn_on_off >( m ); pause( 10 );

so_5::send< blinking_led::turn_on_off >( m ); pause( 5 );

so_5::send< blinking_led::turn_on_off >( m ); pause( 5 );

env.stop(); } );}

64

Page 65: Модель акторов и C++ что, зачем и как?

Заключение 1/3Модель Акторов – это очень удобный инструмент в случаях, где использование этой модели уместно1.

Это уже неоднократно доказывалось успешным применением таких инструментов, как Erlang и Akka в самых разнообразных проектах.

Да и вообще за асинхронным обменом сообщений между независимыми сущностями будущее. Прислушайтесь к Джо Армстронгу, он плохого не посоветует ;)

1) Не верьте рекламе: уместно не везде.

65

Page 66: Модель акторов и C++ что, зачем и как?

Заключение 2/3Наш опыт показывает, что при наличии подходящих инструментов Модель Акторов имеет смысл применять и в C++.

При этом для C++ подходящие готовые инструменты уже есть.

На разный вкус и цвет.

И размер кошелька, конечно же.

В коммерческом проекте за QP/C++ и за Just::Thread Pro придется заплатить.За SObjectizer и CAF – нет. По крайней мере сразу не придется.

66

Page 67: Модель акторов и C++ что, зачем и как?

Заключение 3/3Вот чего делать не стоит, так это браться за написание собственного акторного фреймворка.

Неблагодарное это дело. Проверено. На людях.

Лучше все-таки взять что-то готовое. Hint: купляйце беларускае.

И пусть кто-нибудь другой весело бегает по граблям многопоточности :)

67

Page 68: Модель акторов и C++ что, зачем и как?

Спасибо за терпение!

Вопросы?

68

Page 69: Модель акторов и C++ что, зачем и как?

Bonus track (SO-5's fixed_stack, 1)#include <iostream>

#include <so_5/all.hpp>

class fixed_stack final : public so_5::agent_t{ state_t st_empty{ this }, st_filled{ this }, st_full{ this }; const size_t m_max_size; std::vector< int > m_stack; public : class empty_stack final : public std::logic_error { public : using std::logic_error::logic_error; };

struct push { int m_val; }; struct pop : public so_5::signal_t {};

69* https://bitbucket.org/sobjectizerteam/fixed_stack_example

Page 70: Модель акторов и C++ что, зачем и как?

Bonus track (SO-5's fixed_stack, 2) fixed_stack( context_t ctx, size_t max_size ) : so_5::agent_t( ctx ) , m_max_size( max_size ) { this >>= st_empty; so_subscribe_self() .in( st_empty ) .in( st_filled ) .event( &fixed_stack::on_push ); so_subscribe_self() .in( st_filled ) .in( st_full ) .event( &fixed_stack::on_pop_when_not_empty ); so_subscribe_self() .in( st_empty ) .event( &fixed_stack::on_pop_when_empty ); }

70

Page 71: Модель акторов и C++ что, зачем и как?

Bonus track (SO-5's fixed_stack, 3)private : void on_push( const push & w ) { m_stack.push_back( w.m_val ); this >>= ( m_stack.size() == m_max_size ? st_full : st_filled ); } int on_pop_when_not_empty( mhood_t< pop > ) { auto r = m_stack.back(); m_stack.pop_back(); this >>= ( m_stack.empty() ? st_empty : st_filled ); return r; } int on_pop_when_empty( mhood_t< pop > ) { throw empty_stack( "empty_stack" ); }};

71

Page 72: Модель акторов и C++ что, зачем и как?

Bonus track (SO-5's fixed_stack, 4)int main() { try { so_5::launch( []( so_5::environment_t & env ) { so_5::mbox_t stack; env.introduce_coop( [&stack]( so_5::coop_t & coop ) { stack = coop.make_agent< fixed_stack >( 5u )->so_direct_mbox(); } );

for( int i = 0; i < 10; ++i ) so_5::send< fixed_stack::push >( stack, i );

std::cout << "stack { "; try { for(;;) std::cout << so_5::request_value< int, fixed_stack::pop >( stack, std::chrono::seconds(10) ) << " "; } catch( const fixed_stack::empty_stack & ) {} std::cout << "}" << std::endl;

env.stop(); } ); return 0; } catch( const std::exception & x ) { std::cerr << "Oops! " << x.what() << std::endl; } return 2;}

72

Page 73: Модель акторов и C++ что, зачем и как?

Документация по 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.

Блог автора доклада: eao197.blogspot.comО SObjectizer в блоге: eao197.blogspot.com/search/label/SObjectizer

Почта автора доклада: eao197 на gmail тчк com

73