Как стать автором
Обновить
87
0.2
Евгений Охотников @eao197

Велосипедостроитель, программист-камикадзе

Отправить сообщение
слишком мутное описание, чтобы из него можно было что-то почерпнуть об алгоритме.

Так описания алгоритма и не было. Речь о том, что когда идет интенсивный поток заявок, а обработка каждой заявки занимает очень небольшое время, то на практике очень хорошо зарекомендовал себя подход Disruptor.
Есть ли в SObjectizer статическая типизация агентов? Как это сделано у Вас?

Не понятно, что подразумевается под статической типизацией. Посмотрите пример из статьи. Метод fixed_stack::on_push будет вызван только для сообщения push, ни для какого другого сообщения его использовать не получится. Возможно это как раз посредством статической типизации.

Вероятно, вы под статической типизацией понимаете что-то другое. Например, невозможность отправить агенту сообщение типа A, если агент в нем не заинтересован. Если так, то этого в SObjectizer нет. Поскольку на практике с такими задачами не встречались. Где это может потребоваться?

Подразумевается, что актор может быть как функцией, так и классом. К чему требовать классы там, где достаточно лябды?

Прекрасно, нет проблем. Можно так:
class onliner_demo final : public so_5::agent_t {
public :
  onliner_demo(context_t ctx) : so_5::agent_t(std::move(ctx)) {
    so_subscribe_self()
      .event( &onliner_demo::event_A )
      .event( &onliner_demo::event_B );
  }
private :
  void event_A(const A &) {...}
  void event_B(const B &) {...}
};

Можно так:
auto coop = env.create_coop("demo");
auto onliner_demo = coop.define_agent();
onliner_demo.event( onliner_demo, [](const A &) {...});
onliner_demo.event( onliner_demo, [](const A &) {...});

Причем для самого SO-5 разницы между этими агентами никакой нет, поскольку во втором случае неявным образом конструируется обычный наследник agent_t.

Соответственно, вне зависимости от вида оформления агента ему доступны все, что доступно агенту (подписки, привязка к диспетчерам и т.д.).
не всегда возможно запихнуть всё приложение в модель акторов (gui, например), поэтому необходим тип акторов, который живёт «вовне» (scoped_actor) и управляется внешним event-loop

Тут непонятно зачем такие сущности обзывать акторами и делать для них какие-то специальные варианты. Либо есть актор, который привязывается к соответствующему диспетчеру (а диспетчер можно сделать так, чтобы он работал на GUI-нити или на другой нити со своим event-loop-ом). Либо это вообще не актор, а просто внешняя сущность, которой нужно тем или иным образом с акторами взаимодействовать. Опять же, это решается без выделения особых типов акторов.
Для работы внутри системы акторов также вохможны 2 ситуации:
— работа, основанная на сообщениях от других акторов (event_based_actor)
— работа, требующая ручного управления событиями внутри актора (сокеты, железо, бд и тп) (blocking_actor)

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

В SObjectizer контекстами управляет диспетчер, к какому агента привяжут, так он и будет работать. Нужен агенту эксклюзивный контекст — он привязывается либо к active_obj-диспетчеру, либо к собственному one_thread-диспетчеру. При этом внешний вид актора и его внутреннее поведение никак не меняется.
В SObjectizer настолько прост, что в нём ненужно разбираться?

Люди говорят, что проще, чем CAF.
Я вам больше скажу :)
Ключевым фактором является «тяжесть» обработки сообщения. Чем короче время обработки, тем меньше выгод в разнесении обработчиков по разным потокам. Даже при высокой интенсивности потока сообщений.

При очень высокой интенсивности и совсем дешевой обработке, вообще актуальность использования Модели Акторов под вопросом. Тут может лучше работать подход на основе Disruptor-а.

Что касается управления контекстами, то сейчас в SObjectizer восемь типов готовых диспетчеров и разработчик может распределять своих акторов между ними как ему захочется, в том числе выбирая конфигурацию прямо в run-time. Чему способствует так же и то, что в SObjectizer всего один вид агентов, а не зоопарк, как в CAF :)

Ну и на счет зоопарка в CAF. Документация говорит, что там есть где развернуться:
CAF provides several actor implementations, each covering a particular use case. The available implementations differ in three characteristics: (1) dynamically or statically typed, (2) class-based or function-based, and (3) using asynchronous event handlers or blocking receives.

И, скажем, Pub/Sub на базе CAF-овских групп доступен для dynamically typed акторов, но не для statically typed.

Вероятно, кому-то доставляет удовольствие со всем этим разбираться :)
Не акторы, а акторный фреймворк. Как раз это один из факторов, благодаря которому разработка многопоточного софта с использованием акторных фреймворков оказывается проще, чем на «голых» нитях/мутексах. Программист берет готовый, отлаженный и опробированный в разных проектах инструмент и получает работающие mpsc/mpmc очереди «из коробки». Ему не нужно очереди программировать вручную, отлаживать их и т.д.

Причем этот бонус разработчик получает не только в случае использования акторных фреймворков, но и в случае использования фреймворков с реализации других подходов к решению проблем concurrent computing: будь то реализация модели Pub/Sub или CSP. Но акторные фреймворки, особенно те, в которых актор — это не поток ОС, а объект или короутина, дают еще одно преимущество: они берут на себя задачу диспетчеризации событий для акторов. Тем самым снимая с разработчика еще и заботы об организации рабочих потоков, распределение задач по ним и т.д.
> Текст претендует на некоторое сравнение существующих фреймфорков

Вам показалось, не претендует.

> Но при этом не содержит упоминания о реализуемых моделях в других реализациях.

Про механизм групп в CAF у меня вообще не было представления, мне казалось, они там вообще для каких-то других целей использовались. Может быть это какое-то свежее добавление в 0.15.

CAF-ом никогда не пользовался, приходилось на него смотреть изредка, когда вот с подобными комментариями сталкивался. Что знал, рассказал. У кого есть опыт, пожалуйста, делитесь. Наверняка получится интереснее и подробнее, чем у меня.
А, понятно.
Боюсь, там это будет все-таки оффтопик.
Говорить сейчас про Модель Акторов и не упомянуть про Erlang просто нельзя.
Но вот в какой блог нужно было разместить данную статью я так и не понял.
Было бы хорошо, если бы тот, кто имеет положительный опыт использования CAF-а, рассказал бы об этом фреймворке русскоязычным читателям в отдельной статье.
Ну и где речь идет об «игнорировании платформы»? Значимой платформой Windows для них не была, они сами об этом, по-моему, в своем блоге писали. Мол, чем тратить время на борьбу с заморочками msvs, мы лучше сделаем что нам нужно на самых свежих версиях gcc и clang.

Что до 2017-го года, то, к сожалению, еще есть проекты, которые пока еще и не думают на vc 14.0 переходить, хорошо, если хотя бы на vc 12.0 сидят, а то могут и на более старых компиляторах оставаться.

В общем, если вам нравится CAF, то уж простите, что я высказался в их адрес столь жестко. Но я видел развитие событий именно таким образом.
Наверное потому, что я не в курсе, что за блог Erlang/OTP.
А разве было сказано, что CAF не компилируется msvc?

Нормально он стал компилироваться после выхода VC 14.0, до этого под Windows они рекомендовали пользоваться MinGW.
Хм… Никогда не задумывался на эту тему. Насколько я помню, при работе с ZeroMQ нужно самостоятельно извлекать сообщения из канала, определять их тип и искать подходящий обработчик для сообщения. Т.е. приходится вручную делать то, что акторный фреймворк делает автоматически. С этой точки зрения я бы не стал причислять ZeroMQ к реализациям Модели Акторов.

А вот в TIBCO Rendezvous, емнип, брала на себя заботы не только по доставке сообщений через транспортные каналы, но и по диспетчеризации листенеров сообщений. В этом плане листенеры в TIBCO Rendezvous похожи на акторов, а сам TIBCO Rendezvous может рассматриваться одна из возможных реализаций Модели Акторов. Хотя TIBCO Rendezvous больше относится к модели Publish/Subscribe, имхо.
Мне вы зачем этот вопрос задаете? Такой вариант первоначально был приведен автором статьи как рецепт для «улучшения кода». Только этот рецепт оказался недействительным, т.к. он не компилируется.

Если же вас интересует, почему в C++ вообще можно объявлять локальные переменные внутри условия if-а, то это сделано для удобства. Объявленная в if-е переменная будет видна только в ветках then и else. Т.е. для вот такого кода:
int a = 0;
if(int b=(a+1)) {
  ...
}
else {
  ...
}

полным аналогом будет вот такой код:
int a = 0;
{
  int b = a+1;
  if(b) {
    ...
  }
  else {
    ...
  }
}

про эти лишние фигурные скобочки забывают, невольно продляя время жизни и область видимости для b. Что может быть чревато, если у b тип не такой простой, как int, а с нетривиальным деструктором.
А вы уверены, что вот эта ваша рекомендация:

if ((uint32 roll = urand(0, 99)) < 3)

Вообще скомпилируется?
Очевидно, что делать эти трюки внутри цикла вызовов mg_mgr_poll() — это ерунда. Интересует есть ли возможность зашедулить Mongoose какое-то пользовательское событие из другой рабочей нити. Чтобы Mongoose сам внутри mg_mgr_poll дернул callback с нужным флагом.
Ну так как раз в этом и вопрос: как из другого рабочего потока сказать Mongoose, что у меня есть данные на отправку, чтобы на своем потоке Mongoose дернул нужный коллбэк с нужным событием?
Это нужно делать на той же самой нити, где дергаются обработчики? Или можно с другой?
Огромной ложкой дегтя остается лицензия – GPLv2, а ценник на коммерческую лицензию для небольших проектов кусается.


Вроде как для тех, кого это не устраивает, есть civetweb, который является форком Mongoose от 2013-го года (когда Mongoose сменил лицензию).

Но меня вот какой вопрос интересует: если при обработке события MG_EV_HTTP_REQUEST не выставить флаг MG_F_SEND_AND_CLOSE, а передать запрос на обработку куда-то еще, то как отдать результат обработки в соединение когда этот результат будет таки получен (например, спустя 10 секунд после вызова обработчика)?
К сожалению, это не мой аргумент, а те рекомендации, которые рекомендуются к использованию уже пару десятков лет… :(
Ага.

Со временем подобный код превращается во что-то такое:
Gdiplus::Bitmap* bitmap; // (1)

    // Инициализация GDI+.
    Gdiplus::GdiplusStartup(&token, &input, &output);

    // Еще какие-то действия, которые появились в последствии.
    ...

    bitmap->SetSomeProperty(...); // Упс №1: bitmap еще не создан.
        // Из-за того, что в (1) переменная bitmap даже не получила
        // нулевого значения может произойти все, что угодно.

    // Еще какие-то действия, которые добавились позже.
    ...

    bitmap = new Gdiplus::Bitmap(filename); // Вот и создали объект.

    int w = bitmap->GetWidth();
    int h = bitmap->GetHeight();
    
    // Тут со временем появились какие-то проверки.
    if( w > SOME_LIMIT || h > SOME_ANOTHER_LIMIT )
      return; // Упс №2: утекла память, т.к. delete сделать забыли.

    for (int i = 0; i < w; i++)
        for (int j = 0; j < h; j++)
        {
           ...
        }
    delete bitmap;

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

Ничего бы этого не было, если бы вы просто написали:
Gdiplus::Bitmap bitmap(filename);


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

Ну вот так в последствии C++ный код начинает и тормозить, и течь, и падать.

Зато разработчики PVS-Studio без работы и клиентов не останутся :)

Информация

В рейтинге
2 162-й
Откуда
Гомель, Гомельская обл., Беларусь
Зарегистрирован
Активность