LINUX.ORG.RU

SObjectizer-5.6.0: новая мажорная версия акторного фреймворка для C++

 , , ,


0

2

SObjectizer — это относительно небольшой фреймворк для упрощения разработки сложных многопоточных приложений на C++. SObjectizer позволяет разработчику строить свои программы на базе асинхронного обмена сообщениями с использованием таких подходов, как Actor Model, Publish-Subscribe и CSP. Это открытый проект под лицензией BSD-3-CLAUSE. Краткое впечатление о SObjectizer можно составить на основании вот этой презентации.

Версия 5.6.0 является первым мажорным релизом новой ветки SObjectizer-5.6. Что означает также завершение развития ветки SObjectizer-5.5, которая развивалась более четырех лет.

Поскольку версия 5.6.0 открывает новую главу развития SObjectizer, то нововведений совсем нет в сравнении с тем, что было изменено и/или удалено из SObjectizer. В частности:

  • используется C++17 (ранее обходились подмножеством C++11);
  • проект переехал и живет теперь на BitBucket с официальным, а не экспериментальным, зеркалом на GitHub;
  • у коопераций агентов нет больше строковых имен;
  • из SObjectizer удалена поддержка синхронного взаимодействия между агентами (его аналог реализован в сопутствующем проекте so5extra);
  • удалена поддержка агентов ad-hoc;
  • для отсылки сообщений теперь используются только свободные функции send, send_delayed, send_periodic (старые методы deliver_message, schedule_timer, single_timer из публичного API изъяты);
  • функции send_delayed и send_periodic теперь имеют единый формат вне зависимости от типа получателя сообщения (будь то mbox, mchain или ссылка на агента);
  • добавлен класс message_holder_t для упрощения работы с преаллоцированными сообщениями;
  • удалено множество вещей, которые были помечены как deprecated еще в ветке 5.5;
  • ну и еще всякое разное.

Более развернутый список изменений можно найти тут. Там же, в Wiki проекта, можно найти документацию по версии 5.6.

Архивы с новой версией SObjectizer можно взять на BitBucket или на SourceForge.

PS. Специально для скептиков, которые считают, что SObjectizer никому не нужен и никем не используется. Это не так.

>>> Подробности

★★★★★

Проверено: Shaman007 ()
Последнее исправление: Virtuos86 (всего исправлений: 3)

Ответ на: комментарий от rGlory

Это вопрос терминологии.

Нет. Если один рабочий поток может обслуживать несколько объектов, то объекты вынуждены поддерживать кооперативную многозадачность.

1 - Y должен уметь обрабатывать асинхронные сообщения. 2 - Y не должен блокироваться при обработке сообщений.

Ничего подобного. Асинхронность важна для X: он не должен блокироваться на операции передачи сообщения Y. Будет ли блокироваться Y на получении и обработки сообщения не суть важно.

eao197 ★★★★★
() автор топика
Ответ на: комментарий от Kuzy

Что такое синхронный и асинхронный по-твоему?

Грубо говоря вот синхронный:

   try {
      data = object.getData();
   }
   catch( const std::exception &ex ) {
       ...
   }

вот асинхронный

switch( event->type ) {
    case DataArrived: data = event->data; break;
      state = idle;
    case Timeout : // error message
      state = error;
}
rGlory
()
Последнее исправление: rGlory (всего исправлений: 1)
Ответ на: комментарий от eao197

Будет ли блокироваться Y на получении и обработки сообщения не суть важно.

Ога не важно. Так как Y владеет экслюзивными данными блокировка при обработке рано или поздно поставит колом всю систему. Иначе нахрен нам вообще многопоточность изначально? Если Y блокируется в итоге мы получаем однопоточную систему с гемором в виде ассинхронных сообщений.

rGlory
()
Последнее исправление: rGlory (всего исправлений: 1)
Ответ на: комментарий от rGlory

Повторяю еще раз: асинхронность означает, что X, которому что-то нужно от Y, не блокируется при доставке сообщения Y. В примере выше вы как раз показали что происходит на стороне X, а не Y.

Пока вы это не поймете, вы и будете нести чушь в духе «Если Y блокируется в итоге мы получаем однопоточную систему с гемором в виде ассинхронных сообщений.»

eao197 ★★★★★
() автор топика
Ответ на: комментарий от eao197

Пока вы это не поймете, вы и будете нести чушь

Да нет, это пока вы не поймете, что я понимаю больше чем вам кажется, это вы будете нести чушь. Еще раз повторю: Y должен обрабатывать сообщения быстро, иначе вся система превратится в усложненную однопоточную программу. Если вам это не понятно, то хочется спросить - вы сами сложные программы на своей библиотеке строили? У вас акторы блокировались в обработке сообщений? Очень интересно послушать.

rGlory
()
Ответ на: комментарий от rGlory

Да нет, это пока вы не поймете, что я понимаю больше чем вам кажется, это вы будете нести чушь. Еще раз повторю: Y должен обрабатывать сообщения быстро, иначе вся система превратится в усложненную однопоточную программу. Если вам это не понятно, то хочется спросить - вы сами сложные программы на своей библиотеке строили? У вас акторы блокировались в обработке сообщений? Очень интересно послушать.

Автор SO строил сложные приложения на своей библиотеке и разбирался с блокировками и перегруженностью потока, обслуживающего агентов. Только ленится понять о чем ты тут пишешь и понтуется вместо нормального объяснения (потому и терки тут возникают, что для тебя один объект - один поток, а у него может быть много объектов, которые по сути можно рассматривать как один объект, если у них реализована нормальная кооперативная многозадачность, чтобы они друг друга не тормозили).

В SObjectizer'е потоки, на которых работают объекты (они же агенты в терминах SO) управляются диспетчерами. Есть несколько видов диспетчеров: один поток для каждого объекта (агента), несколько объектов на один поток (вот тут то и нужна кооперативная многозадачность), несколько объектов на пул потоков (вот это и может быть решением ситуации, когда поток Y не справляется со всеми агентами), а также другие диспетчеры. Каждый агент, даже если привязан к диспетчеру с пулом потоков может в один момент времени работать только на одном потоке (т.е. работает только один экземпляр объекта). Кроме суперагентов, которые могут работать параллельно, обрабатывая разные сообщения на разных потоках (также может помочь в случае, когда поток Y не успевает, например, назначить этому объекту-агенту диспетчер с пулом потоков и разрешить его выполнять параллельно, если такое возможно).

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

Так что задачу, когда поток Y тормозит все приложение, потому что загружен на 100% можно попробовать решить разными способами (часть из них встроена в SObjectizer). Но если эта ситуация типа нам надо писать данные в базу в одно подключение, а база не успевает, то тут никакая архитектура приложения не поможет. Потому что скорость обработки информации ограничивается скоростью работы базы. И да, приложение станет однопоточным с кучей асинхронщины, которая еще и защищена мьютексами при каждом обращении к очереди сообщений каждого агента.

anonymous
()
Ответ на: комментарий от rGlory

что я понимаю больше чем вам кажется

Для этого нужны, по меньшей мере, какие-то понятные и последовательные комментарии с вашей стороны.

Еще раз повторю: Y должен обрабатывать сообщения быстро, иначе вся система превратится в усложненную однопоточную программу.

Вообще-то до сих пор вы говорили о блокировках потока Y. Давайте я вам расскажу, что понимается под блокировками.

Блокировка – это когда поток приостанавливается до наступления какого-то внешнего события и ничего не делает пока это событие не наступит.

Примитивный пример для иллюстрации:

class ProtectedSharedData {
   std::mutex lock_;
   ...
public:
   void f() {
      std::lock_guard l{lock_};
      ...
   }
   void g() {
      std::lock_guard l{lock_};
      ...
   }
}

void threadX(ProtectedSharedData & d) {
   d.f();
}

void threadY(ProtectedSharedData & d) {
   d.g();
}

Вот здесь нить threadX, если она стартовала раньше, захватывает мутекс объекта d. И, когда тоже самое пытается сделать threadY, то threadY блокируется на вызове d. Т.е. она приостановлена и ничего (т.е. вообще ничего) не может делать пока не произойдет внешнее событие – threadX выйдет из вызова d.f().

В контексте разговора об очередях сообщений для взаимодействия между рабочими потоками можно придумать еще один вариант блокировки потока Y (именно что придумать, т.к. делают такое разве что малоопытные разработчики или же это происходит по недосмотру):

class ThreadSafeQueue {
   std::mutex lock_;
   std::condition_variable cv_;
   std::deque<Message *> queue_;
   ...
public :
   void push(Message * m) {
      std::lock_guard l{lock_};
      queue_.push_back(m);
      ...
   }

   template<typename Lambda>
   void handle(Lambda && l) {
      std::unique_lock l{lock_};
      ... // Проверка пустоты и ожидания на пустой очереди.
      Message * m = queue_.front();
      queue_.pop_front();
      // Внимание! Вызов обработчика при захваченном lock_.
      l(m);
    }
}

void threadX(ThreadSafeQueue & q) {
   while(true) {
      q.push(...);
      ...
   }
}

void threadY(ThreadSafeQueue & q) {
   while(true) {
      ...
      q.handle([&](Message * m) {...});
   }
}

Здесь суть в том, что threadY блокирует очередь на момент обработки очередного сообщения. И в этот момент никто не может поставить заявку в очередь threadY до тех пор, пока threadY не завершит обработку ранее извлеченной заявки. При этом, строго говоря, поток threadY не блокирован, но выполняет полезную работу. Но threadY блокирует другие потоки, которые не могут выставить свои заявки.

Обсуждения вот таких вещей ожидаешь, когда вы говорите про блокировки потока.

Но, оказывается, вы не о блокировках. А о классической проблеме producer-consumer, когда producer генерирует работу для consumer-а с большим темпом, чем consumer способен обработать. Это отдельная проблема и о ней нужно будет написать отдельный комментарий.

eao197 ★★★★★
() автор топика
Ответ на: комментарий от rGlory

Y должен обрабатывать сообщения быстро, иначе вся система превратится в усложненную однопоточную программу.

Так вот о проблеме producer-consumer. Эта проблема возникает, когда producer может сгенерировать больше заявок, чем способен обработать consumer. Причем эта проблема становится актуальной лишь тогда, когда между producer и consumer возникает некоторая очередь, способная вобрать в себя большое количетсво заявок.

Если такой очереди нет, то нет и проблемы, т.к. producer не может сгенерировать новую заявку, пока consumer не завершит обработку предыдущей.

Итак, раз есть проблема, значит есть и очередь.

И для решения этой проблемы могут применяться разные способы. Например, очередь может быть не безразмерной, а фиксированной. И при попытке поставить заявку в полную очередь producer будет заблокирован до особождения места в очереди. Такой механизм, например, применяется в каналах Go.

Этот же механизм может применяться и в SObjectizer-е с использованием mchain-ов (которые суть CSP-шные каналы, в чем-то аналогичные Go-шным). Т.е. если threadX общается со threadY через mchain фиксированного размера, то при попытке поставить новое сообщение в mchain поток threadX будет приостановлен. Хотя в SObjectizer-е возможны и другие реакции на попытку отослать сообщение в полный mchain.

Но если у нас не threadX и threadY, а actorX и actorY, и общаются они через свои штатные message-box-ы, то ситуация становится сложнее, т.к., в отличии от mchain-а, штатный message-box агента не имеет ограничений на количество сообщений. Поэтому actorX может наотсылать actorY больше сообщений, чем actorY в принципе может обработать.

Это хорошо известная для модели акторов проблема overload control. Решать которую можно по-разному, и в SObjectizer есть несколько готовых инструментов «искаропки».

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

eao197 ★★★★★
() автор топика
Ответ на: комментарий от rGlory

Если вам это не понятно, то хочется спросить - вы сами сложные программы на своей библиотеке строили? У вас акторы блокировались в обработке сообщений? Очень интересно послушать.

Представляете, строил. А если бы не строил, то не было SObjectizer-а в его нынешнем виде, с его нынешними возможностями.

Посему несколько слов о то, какие бонусы мы имеем, если используем для общения между сущностями именно асинхронные сообщения. Под сущностями будут пониматься как рабочие потоки (или fibers/coroutines/goroutines), так и акторы в виде объектов с callback-ами.

Под асинхронным понимается то, что когда X делает запрос Y, то X не блокируется на время, пока запрос дойдет до Y и будет обработан Y. Грубо говоря, у X и Y есть асинхронный send и (для простоты) синхронный receive.

Главный бонус в том, что когда X нужно что-то от Y, то X делает асинхронный запрос и может продолжать свою работу до тех пор, пока от Y не придет ответ или пока X не надоест ждать. Получается, что X не блокирован (на тему «блокирован» смотрим здесь), а значит может выполнять какую-то полезную работу.

Т.е. мы можем сделать что-то вроде:

void threadX(...) {
   // Сперва отправляем запрос Y.
   send(Y, ...);
   ... // Далее можем делать что-то свое.
   // Теперь нам нужен результат от Y, ждем его.
   auto reply = receive(...);
   ...
}

Здесь мы снижаем общую длительность выполнения какой-то операции, т.к. часть работы X и Y могут делать параллельно. А вот если бы X делал синхронный запрос к Y, то время распараллеливания работы бы не получилось.

Так же мы можем в ожидании ответа от Y обрабатывать другие команды, которые приходят к X:

void threadX(...);
   // Отдаем запрос Y.
   send(Y, ...);
   // Ждем ответа и обрабатываем другие типы запросов.
   do {
      auto msg = receive(...);
      switch(msg) {
         ...;
      }
   } while(...);
}

Тем самым мы получаем систему, в которой X сохраняет свою отзывчивость даже пока у него нет ответа от Y. А это в определенных прикладных областях очень важно.

При этом асинхронный send и синхронный receive дают нам, при желании, простую возможность программировать в синхронном стиле. Легким движением руки:

auto askY(...) {
   send(Y, ...);
   auto result = receive(...);
   if(!result)
      throw std::runtime_exception(...);
   return *result;
}
...
void threadX(...) {
   try {
      handle(askY(...));
   }
   catch(...) {
      ...
   }
}

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

eao197 ★★★★★
() автор топика
Ответ на: комментарий от rGlory

Ну и чтобы два раза не вставать, еще пару слов про «асинхронную лапшу».

Нужно разделять случаи, когда у нас есть последовательный поток исполнения (т.е. нить ОС или fiber/coroutine) и когда у нас объект с коллбэками.

Это две принципиальных разницы. Поскольку в первом случае у нас есть возможность писать «последовательный» код, в котором на асинхронных операциях можно построить синхронные операции.

А во втором случае нам нужно возвращать управление из коллбэка. Поэтому если на объектах с коллбэками актору X нужно что-то от актора Y, то в одном своем коллбэке актор X делает send(Y, ...). А реакцию на ответное сообщение нужно делать в другом коллбэке. А если нам еще и нужна реакция на отсутствие ответа, то появляется третий коллбэк. А если при этом X может еще и менять свое состояние в ожидании ответа, то и еще коллбэки.

Вот тут-то и появляется та самая асинхронная лапша. Для борьбы с которой могут потребоваться специальные инструменты.

Но главная причина этого вовсе не в асинхронных сообщениях, а именно в акторах в виде объектов с коллбэками. Если акторы представляются нитями или короутинами, то там этой самой «асинхронной лапши» поменьше.

Фокус, однако, в том, что для каких-то предметных областей акторы в виде объектов с коллбэками удобнее. Поскольку, как правило, в этих областях акторы являются конечными автоматами (а временами и сложными иерархическими КА) и должны обрабатывать разные типы входящих сигналов в каждом из своих состояний.

Поскольку корни у SObjectizer-а растут из одной из таких областей, то акторы в SObjectizer-е вот такие вот.

Если акторы не нравятся, что SObjectizer можно использовать с голыми нитями и mchain-ами. Асинхронной лапши будет сильно поменьше.

Короутины SObjectizer не поддерживает.

eao197 ★★★★★
() автор топика

Очередная поделка ненужно.

anonymous
()

Чет мне думается, что архитектура SO растет из слова SCADA, где объекты в виде конечных автоматов (возможно, иерархических) и асинхронная архитектура на сообщениях (коллбеках) с очередями для каждого объекта появляются естественным образом. У мотора нажали кнопку «пуск», пришло сообщение от таймера по которому опрашиваем датчик температуры, пришло сообщение от датчика температуры, думаем что делать, отправляем сообщение кому надо, пришло сообщение от датчика оборотов о чрезмерном падении скорости вращения - отправляем сообщение логгеру и еще кому надо… Для большинства задач, с которыми многие тут работают, типа серверу пришел запрос по сети - начинаем его обрабатывать с кучей асинхронных задач типа обратиться к базе, файловой системе, другому микросервису, асинхронщина с коллбеками не очень подходит, зато подходят другие виды асинхронщины типа корутин или промисов, выполняющихся на пулах потоков. Потому и непонятки возникают. В SCADA есть куча объектов, работающих сами по себе, но обменивающихся сообщениями, а в сервере после прихода запроса по сути появляется граф задач, часть из которых можно сделать асинхронно и тут объекты с очередями для каждого выглядят противоестественно.

IMHO, SO подходит для задач с долгоживущими объектами с состоянием, которые друг от друга зависят, т.е. скада-подобные задачи (потому и единственное использование SO - обслуживание театра). Для большинства задач современных разработчиков SO бесполезен (в текущем виде).

anonymous
()
Ответ на: комментарий от anonymous

Чет мне думается, что архитектура SO растет из слова SCADA

Даже буква S в названии SObjectizer — это дань предыдущей разработке, SCADA Objectizer. С которой все когда-то давным-давно и началось.

потому и единственное использование SO - обслуживание театра

Не единственное. Это единственное, про которое пользователи решили написать публично. И сделали это.

Для большинства задач современных разработчиков SO бесполезен

Если он будет полезен хотя бы на 5% от современных задач, то с учетом количества, объема и разнообразия этих самых задач, нам хватит :)

в текущем виде

А не в текущем виде? И какой вид должен был бы быть тогда?

eao197 ★★★★★
() автор топика
Ответ на: комментарий от eao197

Все не читал, но вставлю свои пять копеек. Асинхронный код с колбеками можно писать и «последовательно» или, говоря более строго, писать «линейно». Для этого существуют всякие синтаксические сахары, такие частные по конкретную задачу как async-await, или общие для практически любых задач как do-expression для монад в Haskell и computation expressions в F# для монад и моноидов. В любой монаде по сути эти колбеки уже есть. Они как раз передаются вторым аргументов монадической связке. Если раскрывает синтаксический сахар сам компилятор, такой как хаскелевский GHC или компилятор F#, то вообще сложно на глаз различить асинхронный код от последовательного. Вот компилятор Rust еще хотят научить раскрывать async-await, но это уже частное решение конкретной задачи в отличие от тех же хаскеля и F#, которые предоставляют общее решение.

А так, модель актора/агента/эрланговского процесса - это очень классная вещь. Столько головной боли уходит при написании серверного кода. Только тут главное меру знать. А так, если есть изменяемое состояние, то агент очень удобен. Если нет изменяемого состояния, то бывает, что предпочтительнее использовать асинхронные вычисления, которые с этими самыми колбеками, но тут уже нужны грамотно настроенные пулы потоков, которые еще иногда называют «контекстами исполнения».

Единственное, что в C++ мне чего-то не нравится, как std::future описан в книгах и спецификациях. Вообще, у меня больше десятка лет такое чувство, что C++ уже давно в роли отстающего. И как-то некрасиво там делаются многие вещи. И колбеки там для future::then какие-то кривые. Ну, ладно! Оставим эту тему.

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

dave ★★★★★
()
Ответ на: комментарий от dave

Для этого существуют всякие синтаксические сахары, такие частные по конкретную задачу как async-await

Когда появляется синтаксический сахар, то разработчик перестает видеть коллбэки.

Единственное, что в C++ мне чего-то не нравится, как std::future описан в книгах и спецификациях.

SObjectizer — это как раз про другой подход к организации работы, нежели async-и и future. Это скорее про то, что я описывал в этой статье: https://habr.com/ru/post/335304/

Суть в том, что разработчик определяет, какие типы операций в программе ему нужны. Далее решает на каких контекстах эти операции будут выполняться. Далее использует возможности SObjectizer-а для передачи информации между операциями.

Изначально операции должны были быть представлены агентами. Но со временем мы добавили и поддержку CSP, так что можно и вообще без агентов.

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

Желаю выдержки, силы воли и неугасающего интереса, чтобы ты мог развивать ваш проект дальше!

Спасибо!

Это хорошо, когда есть такое хобби

Это не хобби.

eao197 ★★★★★
() автор топика
Ответ на: комментарий от eao197

Akka

ох не все так просто со акка акторами.

Не знаю, как в контексте джавы (хотя по примерам, которые я видел, не то чтобы проще было, разве что свои примитивы рисовать не надо), в скале сами акторы решают ток часть проблем. Вообще, там больше речь про связку библиотеки и самого языка с его паттерн матчингом и работой с фьючами.

Из проблем, например, навскидку, за акторами надо следить (делается отдельным бойлерплейтом с супервизором), там нельзя задать политику backpressure (решается через акка стримы, например, но - сюрприз - акторы не рекомендуется использовать, потому что нет гарантии доставки, если она важна), постоянные прохеренные сообщения из за неучтенных кейсов в receive, скорость работы тоже в общем не то чтобы прям реактивная (если важно).

Ну то есть не в смысле все хрень, а, скорее, не нужно преподносить как золотую пилюлю

PS за проектом слежу, интересно в целом, но в реальной жизни использовать не приходилось, задач подходящих не было

arcanis ★★★★
()
Последнее исправление: arcanis (всего исправлений: 1)
Ответ на: комментарий от arcanis

Модель Акторов, в принципе, имеет специфические условия применения. Тут нужно, чтобы и потери сообщений были не критичны, и отсутствие back-pressure не сильно напрягало, и чтобы латентность доставки сообщений до получателя устраивала. Поэтому сама по себе Модель Акторов подходит далеко не всегда.

Я бы добавил сюда еще и то, что в Модели Акторов взаимодействие ведется в режиме 1:1, а если нужно M:N, то нужно делать промежуточных акторов-брокеров и запариваться на подписку/отписку (что с учетом асинхронности и возможных потерь сообщений может изрядно доставить).

Поэтому у нас не только (да и не столько) Модель Акторов во главе угла. Скорее замес из Actors, Publish/Subscribe и CSP. Вот это все вместе и упрощает жизнь разработчику если в его программе оказываются нужны автономные самостоятельные сущности и асинхронное взаимодействие между ними.

Для борьбы с overload у нас есть message-limits, а для back-pressure — mchains. Вообще говоря, back-pressure на асинхронных сообщениях хорошо решается разве что в CSP. Но там же из-за back-pressure можно и на дедлоки нарваться.

Надобность в супервизорах в C++ не такая большая, как в безопасных языках. Тут либо актор работает и не падает, либо падает и утягивает за собой весь процесс. А ситуации, когда падает, но ничего с собой не тянет, являются редкостью.

Потери сообщений из-за неправильных подписок бывают, да. Одна из самых распространенных ошибок. Чтобы помочь с этим мы msg_tracing добавили. Можно отследить, что куда ушло и почему потерялась. Даже при отладке новых фич в SObjectizer-е этим регулярно пользуюсь.

Ненадежность доставки — это не баг, это фича. Асинхронщина вообще с гарантиями доставки не очень дружит. А, по опыту, ненадежность доставки парадоксальным (на самом деле нет) образом повышает надежность приложения.

На реактивность пока не жаловались. Хотя у нас достаточно высокие накладные расходы на доставку и вызов обработчика (это обратная сторона наличия mbox-ов, разнотипных диспетчеров и акторов в виде ИКА). Так что если кому-то нужны десятки миллионов сообщений в секунду на одного актора, то универсальный фреймворк вроде SObjectizer-а не подойдет.

eao197 ★★★★★
() автор топика
Ответ на: комментарий от eao197

Я бы добавил сюда еще и то, что в Модели Акторов взаимодействие ведется в режиме 1:1, а если нужно M:N, то нужно делать промежуточных акторов-брокеров и запариваться на подписку/отписку (что с учетом асинхронности и возможных потерь сообщений может изрядно доставить).

ну конкретно в случае M:N что то типа общей шины (event bus в реализации акка) позволяет решить задачу в ряде случаев (у вас соответственно через producer/comsumer)

Надобность в супервизорах в C++ не такая большая, как в безопасных языках. Тут либо актор работает и не падает, либо падает и утягивает за собой весь процесс. А ситуации, когда падает, но ничего с собой не тянет, являются редкостью.

да, я видел выше и, из своей практики, согласен с таким подходом лол

Спасибо за развернутый ответ, видимо, надо ближе смотреть на +- реальных задачах

arcanis ★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.