Actor-based. Success story
От: Буравчик Россия  
Дата: 27.06.12 19:28
Оценка:
У кого-нибудь есть опыт участия в достаточно больших проектах, архитекутра которых была бы основана на акторах? Т.е. система разбивалась на независимые компоненты, которые обмениваются сообщениями (и только сообщениями). Конечно, вызов методов — это тоже, в некотором роде, посылка сообщений, но интересуют системы, построенные на "настоящих" сообщениях, т.е. компонент имеет очередь сообщений и явно обрабатывает их (как в Erlang).

Какие языки/библиотеки использовались?
Удобно ли сопровождать (понимать, расширять, изменять) такие системы?
Какие есть минусы и плюсы actor-based?
Для чего actor-based стоит применять, а для чего нет?

Поделитесь опытом и мыслями.
... << RSDN@Home (RF) 1.2.0 alpha 5 rev. 17>>
Best regards, Буравчик
Re: Actor-based. Success story
От: Кодёнок  
Дата: 27.06.12 20:26
Оценка: 33 (3)
Здравствуйте, Буравчик, Вы писали:

Б>У кого-нибудь есть опыт участия в достаточно больших проектах, архитекутра которых была бы основана на акторах? Т.е. система разбивалась на независимые компоненты, которые обмениваются сообщениями (и только сообщениями). Конечно, вызов методов — это тоже, в некотором роде, посылка сообщений, но интересуют системы, построенные на "настоящих" сообщениях, т.е. компонент имеет очередь сообщений и явно обрабатывает их (как в Erlang).


Б>Какие языки/библиотеки использовались?

Б>Удобно ли сопровождать (понимать, расширять, изменять) такие системы?
Б>Какие есть минусы и плюсы actor-based?
Б>Для чего actor-based стоит применять, а для чего нет?

Я как-то написал свой actor-machine для Objective-C, используя фишку автоматического создания NSInvocation при посылке нереализованного сообщения. Сообщения настоящие, с очередью, большинство асинхронные. Про расширяемость в том проекте речи не шло (хотя с точки зрения архитектуры, перенести актер на другую машину можно легко, клиенты даже не заметят), зато из проекта полностью исчезла почти все синхронизация потоков, что на фоне предыдущих неуловимых крашей из-за неправильной многопоточности было втройне круто — параллельность есть, проблем нет! Причем нет вообще. В многопоточном программировании, если ты пускаешь новичка-программера исполнить что-нибудь в фоне и не смотришь на результат, жди краш-репортов. В actor-based в худшем случае что-то затормозит или не выполнится. (Сейчас в Mac OS X 10.6 SDK добавили блоки и dispatch_async, так что надобность во многом отпала.) Story может не очень, но success — 146%.

Здесь на форуме был (а может и до сих пор есть) @eao197, который разрабатывал аналогичный фреймворк для C++ и в чем-то серьезном его использовал. Он называл это агентно-ориентированным программированием (можешь поискать в поиске), но это в принципе один хрен то же самое, может разве что без акцента на параллельность.

Главный (для меня) минус в том, что без поддержки и языка и рантайма, оно никогда не будет красивым и удобным, всегда надо что-то руками писать или с чем-то мириться (в случае с моей Obj-C машиной это -10% производительности из-за создания лишнего объекта и полдесятка лишних вызовов). Но это для меня.

По-настоящему серъезные минусы это
— в actor-based архитектуре очень легко получить дедлок;
— баг, который в простой однопоточной реализации проявляется как stack overflow и немедленный краш потока, при разбитии на акторы мутирует в отжирание 100% памяти на эту самую очередь сообщений и в результате краш всего приложения (а также других приложений в системе т.к. к out of memory как оказывается никто из них никогда реально не готов);
— отлаживать их коммуникацию мне до сих пор непонятно как, кроме как писать логи.

Но по сравнению с трудноуловимыми race condition это пустяки, если честно.

Я лично считаю что многопоточность и примитивы синхронизации — это недоразумение и каменный век, типо макроассемблера, который лишь в единичных узких местах оправдан, а будущее за message-passing concurrency. Task-based concurrency библиотеки (что то же самое) в c# и java давно уже отлично себя показывают, при правильном применении подходят почти везде. Многие новые языки (напр. Go, Rust) делают ставку строго на message-passing, вообще не допуская других типов параллельности в принципе. Есть еще STM, который так и не выстрелил нигде, и мне всегда казался костылем в попытке поправить многопоточность, но кто его знает (вроде хаскеллевцы по неуловимой для меня причине надеятся именно на STM, почему?).

Применять стоит везде, где неудобства (см. главный минус) и возможные накладные расходы не представляют проблемы.
Re: Actor-based. Success story
От: vdimas Россия  
Дата: 27.06.12 22:07
Оценка: :))
Здравствуйте, Буравчик, Вы писали:

Б>У кого-нибудь есть опыт участия в достаточно больших проектах, архитекутра которых была бы основана на акторах? Т.е. система разбивалась на независимые компоненты, которые обмениваются сообщениями (и только сообщениями). Конечно, вызов методов — это тоже, в некотором роде, посылка сообщений, но интересуют системы, построенные на "настоящих" сообщениях, т.е. компонент имеет очередь сообщений и явно обрабатывает их (как в Erlang).


Windows? ))

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

Сами сообщение примерно такие: { Target; Code; Map[tag, value] fields; }

Есть предопределенный набор кодов сообщений и тагов, а остальные регистрируются прикладными модулями так же как регистрируются динамические коды сообщений Windows. Target может иметь сложную структуру, либо просто текст с некими name-convention.


Б>Какие языки/библиотеки использовались?


C#, библиотека самописная миниатюрная. Все уже изобретено до нас, см windows. Единственно что — библиотека может использоваться гибко/произвольно, т.е. некий модуль внутри себя может иметь точно такую же очередь сообщений и перенаправлять сообщения в глобальную очередь лишь при необходимости.


Б>Удобно ли сопровождать (понимать, расширять, изменять) такие системы?


Именно расширять удобно. Произвольной функциональностью. Для этого и писалось.


Б>Какие есть минусы и плюсы actor-based?

Б>Для чего actor-based стоит применять, а для чего нет?

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

Еще подход полезен там, где нужна динамичность, чтобы "приложения" можно было загружать и останавливать.
Re[2]: Actor-based. Success story
От: sergeyt4  
Дата: 27.06.12 22:48
Оценка:
Здравствуйте, vdimas, Вы писали:

V>Здравствуйте, Буравчик, Вы писали:


V>Сами сообщение примерно такие: { Target; Code; Map[tag, value] fields; }


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

Вот, к примеру, поставили вы breakpoint в обработчике сообщений. Как узнать, откуда и почему мы сюда попали? В обычном приложении мы видим в отладчике весь stack и можем переключиться на любой frame, увидеть исходный код того места, где был вызов и значения переменных в том месте и понять почему и как мы оказались в данном месте. Даже если была цепочка вызовов.

Еще: как ни крути, в конце концов приходишь к тому, что алгоритм работы модулей программы построен на схеме "запрос-ответ". Синхронно ли, асинхронно ли, но так. А схемы вроде broadcast и notification handlers используются только в event-ах, которые не возвращают результат. Т.е. в 99% случаев есть некий вызов с параметрами и ожидается результат именно этого вызова (а не предыдущего). В actor-based приложениях это как-то реализовано на уровне языка/рантайма или приходится самому руками реализовывать (например, в каждом сообщении передавать уникальный id запроса и "обратный адрес", на который надо выслать ответ) ?
Re: Actor-based. Success story
От: WolfHound  
Дата: 28.06.12 07:06
Оценка: 2 (1) +1
Здравствуйте, Буравчик, Вы писали:

Акторы нарушают SRP.
Ибо очередь сообщений и поток исполнения не связанные между собой сущности. А акторы прибивают гвоздями одно к другому.
Это приводит к реальным проблемам, когда нужно организовать общение с несколькими акторами.
Ибо вместо того чтобы создать по каналу на соседа и точно знать какой ответ пришёл от какого соседа. Придется в каждом ответе протаскивать отправителя и руками разбираться от кого пришёл ответ.
Также начинаются проблемы, если мы хотим дождаться ответа от одного актора, а ответ от другого приходит раньше. Лечится это при помощи selective receive но он сам по себе создает кучу проблем. В частности выборка сообщения из очереди O(N) от размера очереди. Те полный разбор очереди O(N^2).
Данная проблема приводит к тому, что при кратковременном наплыве сообщений мы можем оказаться в ситуации, когда программа только и делает что копается в очереди. При этом очередь будет продолжать расти.

Короче модель каналов из сингулярити наше все.
А если еще и язык поддерживает проверку протоколов, то все совсем хорошо получается.
... << RSDN@Home 1.2.0 alpha 4 rev. 1472>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[3]: Actor-based. Success story
От: vdimas Россия  
Дата: 28.06.12 08:47
Оценка: 9 (1)
Здравствуйте, sergeyt4, Вы писали:

V>>Сами сообщение примерно такие: { Target; Code; Map[tag, value] fields; }


S>Меня больше всего смущает сложность отладки таких приложений.


Наоборот просто, ведь каждый модуль/плагин можно отлаживать независимо по очень простому интерфейсу. Скажем так, уровень атоматизации юнит тестирования модулей такой системы не сравним с обычным юнит-тестированием даже на моках. Из-за однородности интерфейса... Ты только начни писать туда хелперы для тестирования и уже не сможешь остановиться. )) Полностью декларативно и минимально выходит.

И самое главное, кол-во вызовов через этот механизм, в сравненеии с общим кол-вом вызовов, не так жу велико. Это же специальный механизм спайки модулей. Альтернатива DI-контейнерам на жестких интерфейсах с кучей плюшек сверху.

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

S>Вот, к примеру, поставили вы breakpoint в обработчике сообщений. Как узнать, откуда и почему мы сюда попали? В обычном приложении мы видим в отладчике весь stack и можем переключиться на любой frame, увидеть исходный код того места, где был вызов и значения переменных в том месте и понять почему и как мы оказались в данном месте. Даже если была цепочка вызовов.


Если сообщение отправляется в другой контекст/поток, то не можешь ты ничего увидеть в обычном приложении тоже. А в описанном применении по-другому никак. Сетевой модуль или GUI-модуль должен плюнуть сообщением в асинхронню, по отношению к вызываемому, очередь и снова бежать слушать сокет или очередь сообщений GUI. Я не вижу вообще продем с отладкой в этой системе (из реальной работы такое наблюдение). Загрузи в систему только два интересующих модуля — один источник, другой приемник, и отлаживай. Ну и плюс сообщение всегда можно дополнить нужными тебе полями, например Source. Причем, подобное поле может быть как одним из типизированных полей, так и полем в мапе fields[tag, name]. Для отладочных целей никто не мешает в момент отправки сообщения дополнять его отладочными полями, скажем так:
message[Params.StackTrace] = GetStackTrace();

Причем, делается это лишь в одном месте — в кишках очереди сообщений.

А сами эти поля никто не мешает сделать типизированными:
class Params {
    public static Param<int> MinJitter;
    public static Param<bool> Dejitter;
    public static Param<float> AgcLevel;
    public static Param RecordUri;
...
}


С пустым параметром шаблона — это текстовый параметр/поле сообщения. Во время старта через рефлексию эти поля инициализируются значениями в такой манере:
Params::MinJitter = new Param<int>();

Т.е. просто создается identity типизированного аргумента (простоты ради этого не делается при объявлении). Соответственно, положить затем аргумент в сообщение и изъять из него можно будет только типизированное значение, указанное при объявлении identity аргумента.

Ну и пара основных интерфейсов:
    /// @return True to stop processing, false to try other handlers
    public interface IMessageReceiver
    {
        bool Received(Message msg);
    }

    public interface IMessageNotifier : IDisposable
    {
        void Dispatched(Message msg, bool handled);
    }


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

Еще момент — был добавлен приоритет обработчиков, чтобы получающие сообщения чаще всего (и чаще всего останавливающие дальнейший диспатчинг сообщения) были первыми. Но это мелочи лишь для поднятия эффективности. Даёт эффект только когда к системе прицепились сотни/тысячи юзеров и поднялось несколько модулей на каждое соединение.


S>Еще: как ни крути, в конце концов приходишь к тому, что алгоритм работы модулей программы построен на схеме "запрос-ответ". Синхронно ли, асинхронно ли, но так.


Да, поэтому есть механизм синхронной и асинхронной отправки сообщений. Прямо как в виднах. И есть возврат результирующего значения. Для синхроного — это просто дополнения/модификация сообщения, для асинхронного — надо подать делегат, куда придет возвращаемое сообщение (или не подать ничего для игнора).

S>А схемы вроде broadcast и notification handlers используются только в event-ах, которые не возвращают результат.


Глупости. Вот есть приложение, которое хочет установить связь, есть конечное URI, в системе загружено несколько модулей. Ты посылаешь предопределенное сообщение с этим URI, чтобы имеющиеся модули, подписанные на это предопределенное сообщение, его опознали. Кто опознал URI — может прервать цепочку обработки сообщения (опять же, прямо как в Windows) и вернуть некое новое сообщение с распаршенными полями URI и прочими характеристиками подключения.

Просто дело в том, что если расписывать всю подобную функциональность в виде некоего жирного интерфейса и его методов, то, в итоге, сколько будет сообщений, столько методов. Причем, там параметров много обычно, поэтому на каждый метод еще будешь разрабатывать структуры/классы, которые будут аргументами этих методов. Теперь для прикидки: таких методов могут быть многие сотни, а конкретные модули юзают буквально единицы/десятки сообщений. И что характерно, "области" таких методов/сообщений пересекаются, то есть ты не поделишь этот интерфейс более-менее разумно кроме как на буквально интерфейсы с одним методом. Тогда вся "типизированная" кухня будет отличаться от описанной только тем, что вместо обобщенного сообщения ты будешь использовать типизированные сообщения (структуры/классы). Не знаю, насколько это оправдано, если для 99% процентов случаев полями таких классов будут Nullable<int>, Nullable<SomeEnum> и аналогичные... Чем это отличается от показанного динамического варианта? Оно ведь не отличается даже подержкой интеллисенса, бо показанных классов Params довольно много: IaxParams, SipParams, MixerParams и т.д. на каждый прикладной раздел, т.е. пишешь SipParams-точка-выпадает список.

Ну и насчет расширяемости тут даже говорить нечего. Добавить что-либо не трогая остального — проще не бывает в текущем виде. Когда клиенту идут обновления — не надо слать обновления на всю систему, только на обновленные модули.


S>Т.е. в 99% случаев есть некий вызов с параметрами и ожидается результат именно этого вызова (а не предыдущего).


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

S>В actor-based приложениях это как-то реализовано на уровне языка/рантайма или приходится самому руками реализовывать (например, в каждом сообщении передавать уникальный id запроса и "обратный адрес", на который надо выслать ответ) ?


Дык, можно взять готовый асинхронный паттерн дотнета (он не совсем удобен, правда), или использовать свой упрощенный асинхронный паттерн. Курить эту тему самостоятельно, сорри. ))
Думаю, что с новыми асинхронными фишками дотнета, типа await, сейчас можно получить очень простой синтаксис... Хотя и был не так уж сложный.
Re[2]: Actor-based. Success story
От: vdimas Россия  
Дата: 28.06.12 09:24
Оценка: +2 -1 :)
Здравствуйте, WolfHound, Вы писали:

WH>Акторы нарушают SRP.


Наоборот.

WH>Ибо очередь сообщений и поток исполнения не связанные между собой сущности. А акторы прибивают гвоздями одно к другому.


Вот то хорошо, что контекст актора и физический поток исполнения — это могут быть относительно независимые вещи (потоки могут эффективно переиспользоваться). Прямо как в DCOM.

WH>Это приводит к реальным проблемам, когда нужно организовать общение с несколькими акторами.

WH>Ибо вместо того чтобы создать по каналу на соседа и точно знать какой ответ пришёл от какого соседа.

Канал надо как-то создать. Источники и приемники каналов друг о друге заранее не знают. В нашей системе как раз подобным образом создавался канал. Ес-но некое медиа идет затем не через подобную систему, а через специализированные интерфейсы, которые и есть каналы.

WH>Придется в каждом ответе протаскивать отправителя и руками разбираться от кого пришёл ответ.


Не в каждом, а на этапе, который в VoIP назван signaling. А по сути — это динамическая конфигурация некоего графа каналов в ответ на динамические же пришедшие capability каждого ендпоинта.

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


Это нормально, например при сбросе соединения. Тебе уже не нужен тот первый ответ.

WH>Лечится это при помощи selective receive но он сам по себе создает кучу проблем.


Непонятно, о чём ты... "Концептуально" посылка сообщений ничем не отличается от асинхронных вызовов. Всё тоже самое, вид в профиль. Точно такие проблемы и точно такие способы их преодоления. См. чуть более подробно рядом: http://www.rsdn.ru/forum/philosophy/4795831.1.aspx
Автор: vdimas
Дата: 28.06.12


WH>В частности выборка сообщения из очереди O(N) от размера очереди. Те полный разбор очереди O(N^2).


Нет, очередь на то и очередь, что сообщения тупо выбираются по очереди. Даже механизм приоритетных очередей (который реально используется) нифига не O(N), а K*O(1), где K — кол-во уровней приоритета. В общем случае пары уровней достаточно для срочных сообщений.

WH>Данная проблема приводит к тому, что при кратковременном наплыве сообщений мы можем оказаться в ситуации, когда программа только и делает что копается в очереди. При этом очередь будет продолжать расти.


))
Для примера затраты на сигналинг, в сравнении с обработкой медиа — это менее сотой доли одного %. А медиа держит сотни/тысячи подключений.


WH>Короче модель каналов из сингулярити наше все.


Еще раз, каналы надо как-то создать в заранее неопределенной среде. Согласно принципу SRP, каждый актор-модуль понятия заранее не имеет, куда он будет подключен для выполнения работы.И не должен, в этом суть. Это же альтернатива банальному DI-контейнеру в условиях, когда кол-во типов сообщений (и соответствующих типизированных интерфейсов, предназначенных исключительно для сигналинга) переваливает за сотни. Тогда DI-контейнер становится гирей на ногах, перетягивающей к себе внимание от прикладных задач.

WH>А если еще и язык поддерживает проверку протоколов, то все совсем хорошо получается.


Как раз по приведенной ссылке упомянул случай большого кол-ва nullable-значений согласно прикладной задаче. Т.е. даже привычная типизированность в условиях большого кол-ва nullable-значений не играет рояли, ведь это динамика в любом случае. Не сильно большая разница м/у if(message.TryGet(Param.Param1, out value)) vs if(message.Param1.HasValue). Всё равно ко второму варианту зачастую приделывают хелпер наподобие первого варианта, бо с ним удобнее обыгрывать сценарий значений по-умолчанию. У нас еще была такая сигнатура как раз для значений по-умолчанию:
var value = message[Param.Param1, defaultValue];

Намного удобнее в итоге, чем якобы типизированные структуры с 99% nullable полями.
Re[3]: Actor-based. Success story
От: WolfHound  
Дата: 28.06.12 10:14
Оценка: 2 (1) +1
Здравствуйте, vdimas, Вы писали:

WH>>Акторы нарушают SRP.

V>Наоборот.
Мда. Чем дальше. Тем фееричнее.
Разговаривать с тобой нет смысла.
Но проблема в том, что нас дети читают.
Так что придется разобрать глупости.

WH>>Ибо очередь сообщений и поток исполнения не связанные между собой сущности. А акторы прибивают гвоздями одно к другому.

V>Вот то хорошо, что контекст актора и физический поток исполнения — это могут быть относительно независимые вещи (потоки могут эффективно переиспользоваться). Прямо как в DCOM.
1)Ты вообще на что отвечаешь?
2)Какое отношение имеют незначительные детали реализации к модели акторов?

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

V>Канал надо как-то создать. Источники и приемники каналов друг о друге заранее не знают.

Про сингулярити не читал.

V>Не в каждом, а на этапе, который в VoIP назван signaling. А по сути — это динамическая конфигурация некоего графа каналов в ответ на динамические же пришедшие capability каждого ендпоинта.

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

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

V>Это нормально, например при сбросе соединения. Тебе уже не нужен тот первый ответ.
Мне виднее нужен ли мне первый ответ или нет.
Не натягивай свой частный случай на общий.

WH>>Лечится это при помощи selective receive но он сам по себе создает кучу проблем.

V>Непонятно, о чём ты...
В гугле забанили?

V>"Концептуально" посылка сообщений ничем не отличается от асинхронных вызовов. Всё тоже самое, вид в профиль. Точно такие проблемы и точно такие способы их преодоления. См. чуть более подробно рядом: http://www.rsdn.ru/forum/philosophy/4795831.1.aspx
Автор: vdimas
Дата: 28.06.12

Это ты очень примитивные задачи решаешь.
А на самом деле все гораздо сложнее, чем ты говоришь.

WH>>В частности выборка сообщения из очереди O(N) от размера очереди. Те полный разбор очереди O(N^2).

V>Нет, очередь на то и очередь, что сообщения тупо выбираются по очереди. Даже механизм приоритетных очередей (который реально используется) нифига не O(N), а K*O(1), где K — кол-во уровней приоритета. В общем случае пары уровней достаточно для срочных сообщений.
А в очереди, по которой работает selective receive выбор сообщения O(N).
А без selective receive ты не сможешь временно проигнорировать сообщение и дождаться сначала другое.
При этом, когда можно заводить произвольное количество каналов таких проблем не возникает в принципе.

V>Для примера затраты на сигналинг, в сравнении с обработкой медиа — это менее сотой доли одного %. А медиа держит сотни/тысячи подключений.

Ты опять свой примитивный частный случай раздуваешь до общего.

V>Еще раз, каналы надо как-то создать в заранее неопределенной среде. Согласно принципу SRP, каждый актор-модуль понятия заранее не имеет, куда он будет подключен для выполнения работы.

Феерия продолжается.
SRP вообще не о том.
На всякий случай SRP == single responsibility principle.

V>И не должен, в этом суть. Это же альтернатива банальному DI-контейнеру в условиях,

DI-контейнер к SRP никакого отношения не имеет.
Кроме того что DI-контейнер не должен нарушать SRP и не заниматься например рисованием кнопок на форме.

WH>>А если еще и язык поддерживает проверку протоколов, то все совсем хорошо получается.

V>Как раз по приведенной ссылке упомянул случай большого кол-ва nullable-значений согласно прикладной задаче.
Причем тут вообще nullable-значения?
Каналам до лампочки что передавать.

V>Т.е. даже привычная типизированность в условиях большого кол-ва nullable-значений не играет рояли, ведь это динамика в любом случае. Не сильно большая разница м/у if(message.TryGet(Param.Param1, out value)) vs if(message.Param1.HasValue). Всё равно ко второму варианту зачастую приделывают хелпер наподобие первого варианта, бо с ним удобнее обыгрывать сценарий значений по-умолчанию. У нас еще была такая сигнатура как раз для значений по-умолчанию:

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

V>Намного удобнее в итоге, чем якобы типизированные структуры с 99% nullable полями.

Как, обычно не поняв о чем разговор, делаешь глобальные заявления колоссальной глупости.
Не надоело?
... << RSDN@Home 1.2.0 alpha 4 rev. 1472>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[4]: Actor-based. Success story
От: sergeyt4  
Дата: 28.06.12 10:44
Оценка:
Здравствуйте, vdimas, Вы писали:

V>Здравствуйте, sergeyt4, Вы писали:


V>>>Сами сообщение примерно такие: { Target; Code; Map[tag, value] fields; }


S>>Меня больше всего смущает сложность отладки таких приложений.


V>Наоборот просто, ведь каждый модуль/плагин можно отлаживать независимо по очень простому интерфейсу. Скажем так, уровень атоматизации юнит тестирования модулей такой системы не сравним с обычным юнит-тестированием даже на моках. Из-за однородности интерфейса... Ты только начни писать туда хелперы для тестирования и уже не сможешь остановиться. )) Полностью декларативно и минимально выходит.


Допустим, юнит-тесты писать удобно. Но как выяснить, какие модули могут посылать сообщения или ждут сообщений от каких? В обычной программе я нажимаю Shift-F12 на методе интерфейса и получаю список мест, откуда он дергается и кем реализуется. К в актор-based программе это сделать? По коду сообщения? Как выяснить взаимосвязи модулей?

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

Да, с потоками это так. Но когда идет цепочка вызовов, она, как правило, в одном потоке.

Кстати, если использовать WCF, то при "шагании" внутрь сервисного метода отладчик VS2010 умеет автоматически прицепляться к процессу сервиса и останавливаться внутри вызванного метода. Причем стэк в отображается весь — и клиентский и серверный.

В общем, я понял, что actor-based модель удобна для определенного типа приложений.
Re[4]: Actor-based. Success story
От: vdimas Россия  
Дата: 28.06.12 12:22
Оценка: -1 :)
Здравствуйте, WolfHound, Вы писали:

WH>>>Акторы нарушают SRP.

V>>Наоборот.
WH>Мда. Чем дальше. Тем фееричнее.
WH>Разговаривать с тобой нет смысла.
WH>Но проблема в том, что нас дети читают.
WH>Так что придется разобрать глупости.

Почитал... похоже на спор теоретика против всех. ))
Не читали бы вы на ночь советских газет... (С)

WH>>>Ибо очередь сообщений и поток исполнения не связанные между собой сущности. А акторы прибивают гвоздями одно к другому.

V>>Вот то хорошо, что контекст актора и физический поток исполнения — это могут быть относительно независимые вещи (потоки могут эффективно переиспользоваться). Прямо как в DCOM.
WH>1)Ты вообще на что отвечаешь?

На "прибивание гвоздями" (С).

WH>2)Какое отношение имеют незначительные детали реализации к модели акторов?


Это был твой аргумент, вот и раскрывай его.


WH>Ты блин даже модель системы от деталей реализации этой системы отличить не можешь.


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


WH>А самое смешное то, что та модель многопоточности что я описал, может быть реализована также.


Да это вообще не принципиально. Модель многопоточности диктуется выбранным способом решения конкретным задачи, а не дается сверху. Я сразу обратил внимание на то, что библиотечные очереди могут быть использованы не только на самом верхнем уровне, но и на уровне подсистем. Где-то это будет один поток выгребания из очереди и мы получаем однопоточное "приложение" в рамках подсистемы (аналог Windows GUI или COM STA), а где-то — пул потоков будет выгребать, и мы можем получить как мульти-STA, так и ThreadingNeutral — оба на эффективном механизме пула потоков. А перемещение сообщения из глобальной очреди сообщений в некую очередь внутри подсистемы — это полный аналог обычного маршалинга COM при пересечении несовместимых контекстов.


V>>Канал надо как-то создать. Источники и приемники каналов друг о друге заранее не знают.

WH>Про сингулярити не читал.

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


V>>Не в каждом, а на этапе, который в VoIP назван signaling. А по сути — это динамическая конфигурация некоего графа каналов в ответ на динамические же пришедшие capability каждого ендпоинта.


WH>Если не в каждом, то тебе придется делать каналы. О чем ты и пишешь.

WH>Но при этом со мной споришь.

А чего с тобой спорить, если ты опять показываешь, что ты не в теме, а просто теоретизируешь. Но у тебя очень поверхностно выходит. Основные акценты тебе назвали, обрати на них внимание перед следующей итерацией. А ты пытаешься сместить акцент на интересные сугубо тебе вещи — на аналоги yield return из C#, которые в Сингулярити плотнее встроили в язык. Я вообще хз что там обсуждать, ведь они неинтересны с технической т.з., и к тому же, имеют известные ограничения/недостатки. Тут достаточно было одного предложения с твоей стороны "каналы Сингулярити". Всё понятно, говорить не о чем.


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

V>>Это нормально, например при сбросе соединения. Тебе уже не нужен тот первый ответ.
WH>Мне виднее нужен ли мне первый ответ или нет.
WH>Не натягивай свой частный случай на общий.

MTA на сегодня и есть общий случай, в отличие от STA. У более продвинутых получается NeutralThreading Model сверху асинхронных очередей. Уже 3-й раз я привожу аналогии, которые ты должен был бы знать. Откуда вообще берется задача маршаллинга вызовов COM даже в рамках одного процесса, не разбирался еще? Самое время.

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

WH>>>Лечится это при помощи selective receive но он сам по себе создает кучу проблем.

V>>Непонятно, о чём ты...
WH>В гугле забанили?

Непонятно о чем после раскрытия деталей рядом. Даже у реактивной модели есть переходы poll/push, предлагаю покурить для чего именно используют эти переходы и в каких именно местах системы. Это напрямую по обсуждаемой теме. Заодно стоит пофантазировать, а как, собственно, эти переходы могут быть реализованы. Твои предложения?


V>>"Концептуально" посылка сообщений ничем не отличается от асинхронных вызовов. Всё тоже самое, вид в профиль. Точно такие проблемы и точно такие способы их преодоления. См. чуть более подробно рядом: http://www.rsdn.ru/forum/philosophy/4795831.1.aspx
Автор: vdimas
Дата: 28.06.12

WH>Это ты очень примитивные задачи решаешь.
WH>А на самом деле все гораздо сложнее, чем ты говоришь.

Сложнее обычной асинхронности ничего на сегодня нет. И не будет. Но на сегодня многие подводные камни давно обжеванны. Как ты думаешь, зачем в том же дотнете диспетчер сообщений выделен в отдельную сущность и почему у него несколько реализаций как под WinForms так и под WPF? Не писал еще свои диспетчеры? Или, если помнишь, коллега eao197 разрабатывал аналогичную систему для С++ и вокруг чего было больше всего обсуждений? Именно вокруг способов диспатчинга, т.е. механизма доставки сообщений. Но тут ничего интереснее STA, MTA и асинхронности на пуле потоков не придумаешь — остальное будет их комбинаторное сочетание и получающийся в итоге некий маршрут маршалинга (сорри за масло-маслянное) согласно конкретной связи апартментов с конкретными политиками. Иногда довольно-таки нетривиальный маршрут выходит.

WH>>>В частности выборка сообщения из очереди O(N) от размера очереди. Те полный разбор очереди O(N^2).

V>>Нет, очередь на то и очередь, что сообщения тупо выбираются по очереди. Даже механизм приоритетных очередей (который реально используется) нифига не O(N), а K*O(1), где K — кол-во уровней приоритета. В общем случае пары уровней достаточно для срочных сообщений.
WH>А в очереди, по которой работает selective receive выбор сообщения O(N).


Опять и снова курить отличие poll/push моделей. Пусть себе будет O(N) у тех, кто не может выбрать подходящую под задачу модель и привык писать код синхронно.


WH>А без selective receive ты не сможешь временно проигнорировать сообщение и дождаться сначала другое.


Смогу через маленьку прослойку-драйвер.
Queue privateMessages;
...
if(message.target != awaitingChannel) {
  privateMessages.enque(message);
  yeild return;
}

awaitingChannel.push(message);
...
dispatch(privateMessages);


В любом случае выгребать сообщения из общих очередей в нагрузочных сценариях надо обязательно — об это спотыкаешься сразу. Но для случая интрузивной огранизации списков прямо на объектах-сообщениях это бесплатная операция. Правда, на иммутабельных структурах, увы, это будет платная операция. )))


WH>При этом, когда можно заводить произвольное количество каналов таких проблем не возникает в принципе.


Возникает проблема реакций на срочные сообщения по непонятно какому каналу... А твоя — ерундовая, вообще-то для C#, разруливается через yield и получаются в точности такие же каналы, как в сингулярити, через переворачивание исходного синхронного кода в автоматную модель. Но это ограниченная модель сама по себе, т.к. исходный "синхронный" код заведомо ограниченнее явно написанного асинхронного/событийного. Хотя, для некоторых несложных сценариев вполне покатит.

Я напомню, как это делалось раньше для твоих "отдельных каналов" с твоим selective receive:
— создавалось несколько невидимых окошек (ктате, посмотри в своей системе Windows, сколько прямо сейчас у тебя сидит невидимых окошек, сугубо для использования встроенного в винды механизма доставки сообщений);
— выгребание сообщений, если помнишь, по методу poll, и ты можешь именно что дожидаться сообщений на конкретном окне-канале, подавая нужный HWND;
— ты можешь выгребать произвольные сообщения, не только для конкретного окошка, тем самым умея реагировать на срочные сообщения;
— доступна как синхронная модель из-за того, что poll сообщений всегда на совести приложения, а не Windows, так же доступна событийная модель, если привязать обработчки к конкретному хендлу окна.
— потоков может быть несколько, но привязка окна к потоку — жесткая, в итоге имеем STA или мульти-STA.

В общем, фиг с ним с бесполезным теоретизированием, лучше расскажу общий подход к таким вещам при разработке довольно-сложного енжина.
— Основную сложность вижу именно в динамической природе создания связанного графа объектов (инициализация твоих каналов, если хочешь), хотя сами некоторые каналы ес-но типизированы (медиа, например, даже если это медиа передается в другие потоки — а оно всегда так на lock-free очередях). Для привычных мейнстримовый языков — такие типизированные каналы — это просто абстрактные интерфейсы, внутри которых обработчик или очередь-прокси к обработчику в другом потоке; В общем, требуется развитие идеи, заложенной в DI-контейнеры, бо они хороши только для связи примерно десятка сущностей, не более. Ну и плюс не умеют многие вещи, типа разнообразного диспатчинга м/у различными политиками контекстов.
— Платформа понятия не имеет о конкретных интерфейсах, но умеет передавать экземпляры объектов, поддерживающих требуемый интерфейс с гарантией типизированности;
— Некая полезная целевая функциональность в любом случае строго-типизированная, будь то реализация кодека, протокола или просто некоего алгоритма верификации/шифрования;
— Для работы в подобной динамической системе на каждую такую "типизированную" функциональность пишется т.н. драйвер (он у нас так и назывался Driver), в базовой реализации которого уже заложены все плюшки насчет политик диспетчеризации сообщений. Надо только отнаследоваться, выставить необходимые полики и преобразовывать обобщенные сообщения к вызовам целевых алгоритмов/объектов и обратно по событиям оборачиваемого объекта. Драйвер выходит очень "тонким", от десятков строк до ~300 строк для самого сложного в системе (по факту). Но там сложность не интенсивная, а экстенсивная, то бишь за счет кол-ва обрабатываемых сообщений, помимо основных базовых в базовой реализации драйвера. (Все-таки платформа была проблемно-ориентированной)

Сколько я смотрел сложные системы на этот счет — все они идут примерно к такому виду.



V>>Для примера затраты на сигналинг, в сравнении с обработкой медиа — это менее сотой доли одного %. А медиа держит сотни/тысячи подключений.

WH>Ты опять свой примитивный частный случай раздуваешь до общего.

V>>Еще раз, каналы надо как-то создать в заранее неопределенной среде. Согласно принципу SRP, каждый актор-модуль понятия заранее не имеет, куда он будет подключен для выполнения работы.

WH>Феерия продолжается.
WH>SRP вообще не о том.
WH>На всякий случай SRP == single responsibility principle.

Да неужели?


V>>И не должен, в этом суть. Это же альтернатива банальному DI-контейнеру в условиях,

WH>DI-контейнер к SRP никакого отношения не имеет.

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

WH>Кроме того что DI-контейнер не должен нарушать SRP и не заниматься например рисованием кнопок на форме.


DI-контейнер должен лишь помогать решать те дополнительные задачи, которые создает DI-подход к проектированию. Я ХЗ при чем тут кнопка... Ты вообще представляешь типовые задачи DI-контейнеров? А то я ссылался на него как на нечто само-собой разумеющееся... Зря ссылался?

WH>>>А если еще и язык поддерживает проверку протоколов, то все совсем хорошо получается.

V>>Как раз по приведенной ссылке упомянул случай большого кол-ва nullable-значений согласно прикладной задаче.
WH>Причем тут вообще nullable-значения?
WH>Каналам до лампочки что передавать.

Мне показалось, что каналы были упомянуты так же в плане их типизированности, помня твою нелюбовь к динамике.

V>>Т.е. даже привычная типизированность в условиях большого кол-ва nullable-значений не играет рояли, ведь это динамика в любом случае. Не сильно большая разница м/у if(message.TryGet(Param.Param1, out value)) vs if(message.Param1.HasValue). Всё равно ко второму варианту зачастую приделывают хелпер наподобие первого варианта, бо с ним удобнее обыгрывать сценарий значений по-умолчанию. У нас еще была такая сигнатура как раз для значений по-умолчанию:

WH>Нормальные люди для этого используют алгебраические типы данных.
WH>Все сразу становится очень просто и красиво.

nullable и есть алгебраический тип.

WH>А если посмотреть на задачу то скорей всего получится выделить вполне конкретный протокол, разбить сообщение на несколько и вообще избавится от nullable значений.


Не получится. В сетевых протоколах предпочитают давать максимум за раз, сокращая кол-во пинг-понгов для установки связи и договоре о capability и прочих тонкостях. Поэтому 99% полей идут nullable by design. См. хотя бы ASN.1 описание протокола H.323 или семейства T.120-T.13x — там почти всё optional. Отсутствие некоего поля — это тоже важный факт, как и конкретное значение по нему.

V>>Намного удобнее в итоге, чем якобы типизированные структуры с 99% nullable полями.

WH>Как, обычно не поняв о чем разговор, делаешь глобальные заявления колоссальной глупости.
WH>Не надоело?

Дык, в отличие от мистер-теоретика просто делюсь с коллегами конкретными решениями и заодно поясняю, почему именно так. Для того и поясняю, чтобы некие решения не были спроецированы на другой частный случай. ИМХО, моего пояснения вменяемому разработчику должно быть достаточно, т.к. если у него в задаче какой-нить аспект не такой — можно сделать по-другому.

Короче, коль топикстартером вопрос был поставлен о том, стоит ли вообще использовать эту модель или нет, и спрашивалось о фактически-реализованных система, то обсуждать надо лишь их, а не фантазии... а так же интересны акценты трудоемкости и всякие следствия из обсуждаемой модели.. Что я и обсуждаю, в отличие от, приходящих сугубо языком поболтать.
Re[5]: Actor-based. Success story
От: vdimas Россия  
Дата: 28.06.12 12:29
Оценка:
Здравствуйте, sergeyt4, Вы писали:

S>Допустим, юнит-тесты писать удобно. Но как выяснить, какие модули могут посылать сообщения или ждут сообщений от каких? В обычной программе я нажимаю Shift-F12 на методе интерфейса и получаю список мест, откуда он дергается и кем реализуется. К в актор-based программе это сделать? По коду сообщения? Как выяснить взаимосвязи модулей?


А как ты это делаешь в более простом DI-контейнере? А исопльзуешь ли вообще подход DI? Например, используешь ли ты обычные события дотнета вне WinForms (или другого GUI)? А как ты в WinForms узнаешь, кто подпишется на твоё событие? ИМХО, подпишется кто угодно...

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

S>Да, с потоками это так. Но когда идет цепочка вызовов, она, как правило, в одном потоке.

Для такой простой цепочки вызовов в простых сценариях ничего сверху привычного ООП/КОП изобретать ни в коем случае не надо. Вот тут ниже я рассказал, в каком месте появляются подсистемы-акторы: http://www.rsdn.ru/forum/philosophy/4796124.1.aspx
Автор: vdimas
Дата: 28.06.12

(искать по слову драйвер)

S>Кстати, если использовать WCF, то при "шагании" внутрь сервисного метода отладчик VS2010 умеет автоматически прицепляться к процессу сервиса и останавливаться внутри вызванного метода. Причем стэк в отображается весь — и клиентский и серверный.


Ну а я просто запускал две студии — клиента и сервера, бо на WCF свет клином не сошелся. И если я поймал брекпоинт на клиенте, то просто давлю паузу на сервере и всех делов.

S>В общем, я понял, что actor-based модель удобна для определенного типа приложений.


Более того, она удобна для определенного типа сущностей в приложении. Их должно быть заведомое меньшинство, в сравнении со всеми остальными сущностями. Модель акторов — это как модель операционной системы, в которой ты запускаешь взаимодействующие "приложения". Самих приложений — очень немного, в сравнении с сумарным кол-вом сущностей внутри всех приложений.
Re[4]: Actor-based. Success story
От: vdimas Россия  
Дата: 28.06.12 13:04
Оценка:
Здравствуйте, WolfHound, Вы писали:

Кстате, удивительно адекватный контент в вики относительно акторов:

Естественным развитием модели акторов была возможность передачи адресов в сообщениях. Под влиянием сетей с коммутацией пакетов Хьюитт предложил разработать новую модель одновременных вычислений, в которой связь не будет иметь вообще никаких обязательных полей, все они могут быть пустыми. Конечно, если отправитель сообщения желает, чтобы получатель имел доступ к адресам, которых он ещё не имеет, адрес должен быть отправлен в сообщении.

...

Хьюитт был против включения требований о том, что сообщения должны прибывать в том порядке, в котором они отправлены на модель актора. Если желательно упорядочить входящие сообщения, то это можно смоделировать с помощью очереди акторов, которая обеспечивает такую функциональность. Такие очереди акторов упорядочивали бы поступающие сообщений так, чтобы они были получены в порядке FIFO. В общем же случае, если актор X отправляет сообщение M1 актору Y, а затем тот же актор X отправляет другое сообщение M2 к Y, то не существует никаких требований о том, что M1 придёт к Y раньше M2.
...
Например, акторы могут использовать конвейер обработки сообщений. Это означает, что в процессе обработки сообщения M1 актор может варьировать поведение, которое будет использоваться для обработки следующего сообщения. В частности, это означает, что он может начать обработку ещё одного сообщения M2 до завершения обработки M1. На том основании, что актору предоставлено право использования конвейера обработки сообщений, ещё не означает, что он этот конвейер обязан использовать. Будет ли сообщение конвейеризовано или нет — относится к задачам технического компромисса.

...

Идея композиции систем акторов в более крупные образования является важным аспектом модульности, которая была разработана в докторской диссертации Гуля Ага[5], позже развитой им же вместе с Ианом Мейсоном, Скоттом Смитом и Каролин Талкотт.[7]
[5] http://dspace.mit.edu/handle/1721.1/6952
[7] Г. Ага, И. Мейсон, С. Смит, К. Талкотт. Основания для вычислений акторов. Journal of Functional Programming, январь, 1993 (англ.)


Прямо 1-в-1 как получилось в нашей системе, хотя до многого дошли сами в процессе решения возникающих интересных моментов/сценариев.
Re[5]: Actor-based. Success story
От: WolfHound  
Дата: 28.06.12 13:34
Оценка:
Здравствуйте, vdimas, Вы писали:

1)Актор просто по определению это связка потока исполнения и очереди сообщений.
Как это реализовано и сколько акторов живет в одном потоке ОС не важно.
Забудь про COM, MTA, STA и прочую хрень. Это все конкретные детали реализации конкретных систем.
К модели акторов они отношения не имеют.

2)SRP не имеет никакого отношения к DI.
Ибо это универсальный принцип, который должен соблюдаться всегда.
Вне зависимости от того есть DI или нет.

3)DI к асинхронности вообще и акторам в частности не имеет отношения.
Ибо их можно использовать совместно или по отдельности.

4)Если бы ты потрудился понять о чем разговор, то ты бы не писал такие глупости.
WH>>А без selective receive ты не сможешь временно проигнорировать сообщение и дождаться сначала другое.
V>Смогу через маленьку прослойку-драйвер.
V>
V>Queue privateMessages;
V>...
V>if(message.target != awaitingChannel) {
V>  privateMessages.enque(message);
V>  yeild return;
V>}

V>awaitingChannel.push(message);
V>...
V>dispatch(privateMessages);
V>


5)В любой асинхронной системе есть точки синхронизации. И не редко нам нужно дождаться результатов в определенной последовательности.

6)Нет никакой проблемы создавать каналы. Ибо ендпоинты можно передавать через каналы. Ендпоинты можно передавать при создании процесса.
... << RSDN@Home 1.2.0 alpha 4 rev. 1472>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[2]: Actor-based. Success story
От: vdimas Россия  
Дата: 28.06.12 13:50
Оценка: :))
Здравствуйте, Кодёнок, Вы писали:

Кё>По-настоящему серъезные минусы это

Кё>- в actor-based архитектуре очень легко получить дедлок;

Считается ровно наоборот — акторы избавляют от присущему многопоточному ООП склонности к дедлокам. Как можно получить дедлок в асинхронной среде??? Только если ты намеренно отошел от асинхронности и ожидаешь в вечном цикле некоего конкретного события синхронно. Иначе никак...


Кё>- баг, который в простой однопоточной реализации проявляется как stack overflow и немедленный краш потока, при разбитии на акторы мутирует в отжирание 100% памяти на эту самую очередь сообщений и в результате краш всего приложения (а также других приложений в системе т.к. к out of memory как оказывается никто из них никогда реально не готов);


Ну вообще в идеологии акторов сами акторы представлены очень высокоуровневыми сущностями, 99% функциональности которых не выходят за рамки этих сущностей-акторов. Соответственно, кол-во всех багов, возникающих в асинхронных протоколах, делится примерно согласно приведенной пропорции, дай бог 1% или менее.


Кё>- отлаживать их коммуникацию мне до сих пор непонятно как, кроме как писать логи.


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


Кё>Но по сравнению с трудноуловимыми race condition это пустяки, если честно.


???
Не сталкивались ни разу в своей системе. Сдается, у вас была не полноценная система акторов.


Кё>Я лично считаю что многопоточность и примитивы синхронизации — это недоразумение и каменный век, типо макроассемблера, который лишь в единичных узких местах оправдан, а будущее за message-passing concurrency.


Так и есть. Выше некоторого уровня сложности становится невозможно отследить все сценарии взаимной блокировки ресурсов. Хотя, до определенного момента можно бороться через иерархию/приоритеты блокировок. Но их легко случайно нарушить и заблокировать не в порядке иерархии/приоритета. И тогда как выход — начинаешь думать более крупными "мазками", политиками апартментов и маршрутов независимых сообщений, передаваемых асинхронно... Всё уже изобретено до нас 20 лет назад... )))

Кё>Task-based concurrency библиотеки (что то же самое) в c# и java давно уже отлично себя показывают, при правильном применении подходят почти везде.


В дотнете есть от рождения. Создай обычный объект, разметь его нужными атрибутами апартмента COM, создавай потоки, инициализируй их как STA или MTA, и используй встроенный маршаллинг, получив предварительно COM-интерфейс у объекта. Но что-то чуть более хитрое делать намного сложнее — проще написать самим аналоги диспетчеров сообщений.


Кё>Применять стоит везде, где неудобства (см. главный минус) и возможные накладные расходы не представляют проблемы.


Насчет дедлоков, повторю, поклеп. Акторы нужны для избавления от дедлоков в многопоточной среде.
Re[6]: Actor-based. Success story
От: vdimas Россия  
Дата: 28.06.12 14:44
Оценка:
Здравствуйте, WolfHound, Вы писали:

WH>1)Актор просто по определению это связка потока исполнения и очереди сообщений.


Неверно.

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


WH>Как это реализовано и сколько акторов живет в одном потоке ОС не важно.

WH>Забудь про COM, MTA, STA и прочую хрень. Это все конкретные детали реализации конкретных систем.
WH>К модели акторов они отношения не имеют.

Имеют ес-но в качестве демонстрации разнообразия способов доставки сообщений. Это был ответ на "по определению это связка потока исполнения и очереди сообщений". Нет такой связки. Акторы отличаются от обычного ООП асинхронностью и ничем больше, не оговаривая подробностей реализации асинхронности. Хотя, конкретные политики и способы диспетчеризации исследуются на уровне докторских диссертаций. См. список литературы в статье в Вики.

WH>2)SRP не имеет никакого отношения к DI.

WH>Ибо это универсальный принцип, который должен соблюдаться всегда.
WH>Вне зависимости от того есть DI или нет.

Можно узнать, что означает фраза "есть DI или нет"???

DI — это просто подход к проектированию/декомпозиции и не более. По конечному результату, то бишь по декомпозии как продукту дизайнера ПО сложно сказать, было там применено DI или нет. DI — это процесс, а не результат. С помощью этого процеса достигается лучшее SRP, это аксиома. С чем ты споришь-то?


WH>3)DI к асинхронности вообще и акторам в частности не имеет отношения.


DI как трюк разработки нет, есно, а упомянутые мною DI-контейнеры имеют самое непосредственное отношение. См. в той же статье вики тонкость насчет того, как акторы узнают друг о друге. ИМХО, это и есть самая интересная задача в акторах и она выглядит как естественное продолжение задачи, которую решают DI-контейнеры. Ты уже освежил, какую задачу решают эти контейнеры? Раньше их чаще называли IoC-контейнеры, что неграмотно (хоть и прижилось в кач-ве популярного мема... Но некоторые крупные конторы, например Microsoft, пользуются более корректной терминологией).


WH>Ибо их можно использовать совместно или по отдельности.


Трюк DI или асинхронность? Или DI-контейнеры и акторов?


WH>4)Если бы ты потрудился понять о чем разговор, то ты бы не писал такие глупости.


Да я то понимаю и просто получаю фан от наблюдения за твоей работой мысли.. скрипа шестеренок... Но не боись, как показали предыдущие обсуждения, ты довольно быстро "нащупываешь" в чем дело и начинаешь говорить более-менее предметно. Взять хотя бы ту тему о выделении памяти областями или о синхронном GC. Конкретно по этой теме — тебе надо заглянуть в тему DI-контейнеров и у тебя все части обсуждения срастутся сами собой... Гарантирую.


WH>5)В любой асинхронной системе есть точки синхронизации. И не редко нам нужно дождаться результатов в определенной последовательности.


1. Дождаться не значит блочить физический поток.
2. Без подержки срочных собщений, то бишь исключительных ситуаций — это путь к дедлоку. Как известный пример: по приходу того же WM_QUIT бесполезно в обработчике некоего окна вечно ждать некоего специального сообщения, надо тупо освобождать ресурсы и выходить.

Поэтому каждую такую последовательность можно выделить в независимый алгоритм-продолжение, дергающийся в произвольные моменты, то бишь асинхронно извне, но синхронно внутри себя. Примеры популярности использования встроенного виндового механизма для того же самого я уже приводил, запусти Spy++ и полубуйся на все эти скрытые окна WorkerW, message_loop_wnd и прочее... Но это событийная модель, а модель синхронного разворота наподобие yield в C# или аналогичных по-сути каналов сингулярити менее гибка, хотя пойдет для несложных сценариев (повторяюсь уже). И опять же, корректное освобождение ресурсов продолжений в случае простого подвисания (то бишь, больше не подаются тики на шаги продолжения) возможно только на GC. В случае же необходимости детерминированного освобождения ресурсов мне более гибким показалось использование обычной асинхронности с генерацией исключительных ситуаций на уровне входного драйвера. То бишь любой "синхронный" вызов в асинхронной среде — это ни в коем случае не блокировка физического потока, а наоборот — освобождение потока обратно в пул. Современные async/await расшириния C# как раз в правильном направлении. Сравни что происходит с yield/return. Хотя, два подхода можно комбинировать, и получать еще более компактную запись того же самого, что я расписывал без async/await.


WH>6)Нет никакой проблемы создавать каналы. Ибо ендпоинты можно передавать через каналы. Ендпоинты можно передавать при создании процесса.


Гы-гы, это звучит как аналогичное для стандартного ООП: "нет никакой проблемы соединять объекты друг с другом, просто через вызовы методов, установки св-в или через аргументы конструктора". Так для чего же тогда нужны DI-контейнеры? Особенно там, где SRP во главе угла?

Вот ты пока не проникся самой интересной задачей в акторах, считая, что доставка сообщений интересна сама по себе. Неинтересна, это очень низкоуровнево и топорно. По крайней мере у меня на ядро доставки сообщений под 3 политики ушло буквально 3 дня и практически без изменений дожило до продакшена, там нечего делать (возможно, сказался опыт клепания когда-то на VB/DCOM высокоуровневого "клея" для систем бух-учёта, с возможностями в DCOM задания аппартаментов и выделения процессов или колв-а STA-аппартаментов для работы DCOM-компонент, т.е. что происходит и для чего именно оно происходит было известно очень давно).

Хотя взаимные комбинации политик доставки в ирерархических системах акторов порождают любопытные пути прохождения сообщений. Но они любопытны сугубо из эстетических соображений, а так-то всё правильно, и на логике очередности/политики доставки сообщений сидит львиная доля прикладной логики, это неотъемлимая часть протоколов. Зато всё остальное потребовало помозговать... Но это уже было на прикладном уровне. Просто акторы — это совсем другой прикладной уровень, чем привычное ООП, надо думать в терминах общающихся процессов-приложений в гетерогенной среде, где способ общения тоже несет смысловую нагрузку. Но опять же, после многих лет написания ГУИ-приложений асинхронная модель, которая является логическим продолжением событийной, проблем не вызвала.
Re[5]: Actor-based. Success story
От: Mamut Швеция http://dmitriid.com
Дата: 28.06.12 15:07
Оценка: :))
WH>>>>Акторы нарушают SRP.
V>>>Наоборот.
WH>>Мда. Чем дальше. Тем фееричнее.
WH>>Разговаривать с тобой нет смысла.
WH>>Но проблема в том, что нас дети читают.
WH>>Так что придется разобрать глупости.

V>Почитал... похоже на спор теоретика против всех. ))

V>Не читали бы вы на ночь советских газет... (С)

Ну эта. В теории, теория сильнее практики, а на практике...

Тут Wolfhound загоняется насчет selective receive, и я даже знаю, какую ссылку он держит в загашнике. Только вот он работает уже 20 лет в промышленных масштабах, а Singularity с каналами не видно просто тупо нигде.

Но, естественно, именно это является труъ подходом.

Наверное, это тоже в тему: http://cl.ly/111R0x2R343X2d1R0739


dmitriid.comGitHubLinkedIn
Re[3]: Actor-based. Success story
От: Lazin Россия http://evgeny-lazin.blogspot.com
Дата: 28.06.12 15:23
Оценка:
Здравствуйте, vdimas, Вы писали:

V>Здравствуйте, Кодёнок, Вы писали:


Кё>>По-настоящему серъезные минусы это

Кё>>- в actor-based архитектуре очень легко получить дедлок;

V>Считается ровно наоборот — акторы избавляют от присущему многопоточному ООП склонности к дедлокам. Как можно получить дедлок в асинхронной среде??? Только если ты намеренно отошел от асинхронности и ожидаешь в вечном цикле некоего конкретного события синхронно. Иначе никак...


Ну обычно у нас не бывает возможности использовать машины с бесконечным объемом памяти, поэтому приходится использовать ограниченные очереди для акторов, а когда ограниченная очередь переполнена, она блокируется.
Re[7]: Actor-based. Success story
От: WolfHound  
Дата: 28.06.12 15:33
Оценка:
Здравствуйте, vdimas, Вы писали:

WH>>1)Актор просто по определению это связка потока исполнения и очереди сообщений.

V>Неверно.
Это прямо следует из определения по ссылке.

В компьютерных науках модель акторов представляет собой математическую модель параллельных вычислений, которая трактует понятие «актор» как универсальный примитив параллельного численного расчёта: в ответ на сообщения, которые он получает, актор может принимать локальные решения, создавать новых акторов, посылать свои сообщения, а также устанавливать, как следует реагировать на последующие сообщения.


V>Имеют ес-но в качестве демонстрации разнообразия способов доставки сообщений. Это был ответ на "по определению это связка потока исполнения и очереди сообщений".

Это все детали реализации и не более того.

V>Нет такой связки. Акторы отличаются от обычного ООП асинхронностью и ничем больше, не оговаривая подробностей реализации асинхронности.

Ну, так асинхронность то как получается?
А получается она просто.
В каждом акторе живет свой поток.

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

V>DI — это просто подход к проектированию/декомпозиции и не более. По конечному результату, то бишь по декомпозии как продукту дизайнера ПО сложно сказать, было там применено DI или нет. DI — это процесс, а не результат. С помощью этого процеса достигается лучшее SRP, это аксиома. С чем ты споришь-то?

С тем что для того чтобы соблюдать SRP нужен DI контейнер.

V>DI как трюк разработки нет, есно, а упомянутые мною DI-контейнеры имеют самое непосредственное отношение. См. в той же статье вики тонкость насчет того, как акторы узнают друг о друге. ИМХО, это и есть самая интересная задача в акторах и она выглядит как естественное продолжение задачи, которую решают DI-контейнеры. Ты уже освежил, какую задачу решают эти контейнеры? Раньше их чаще называли IoC-контейнеры, что неграмотно (хоть и прижилось в кач-ве популярного мема... Но некоторые крупные конторы, например Microsoft, пользуются более корректной терминологией).

Я это лучше тебя знаю.

WH>>4)Если бы ты потрудился понять о чем разговор, то ты бы не писал такие глупости.

V>Да я то понимаю и просто получаю фан от наблюдения за твоей работой мысли..
А я за тем как ты не можешь понять, где модель системы, а где детали реализации.
Я просто нахожусь на более высоком уровне и вижу лес, а не деревья.

V>скрипа шестеренок... Но не боись, как показали предыдущие обсуждения, ты довольно быстро "нащупываешь" в чем дело и начинаешь говорить более-менее предметно. Взять хотя бы ту тему о выделении памяти областями или о синхронном GC.

То, что я говорил в той теме я понял несколько лет назад.
Только ты так и не понял, что именно я говорю.

V>1. Дождаться не значит блочить физический поток.

Опять путаешь поток ОС и поток актора. Это разные вещи.
Акторы про потоки ОС нихрена не знают. Ибо это деталь реализации. Не более того.

V>2. Без подержки срочных собщений, то бишь исключительных ситуаций — это путь к дедлоку. Как известный пример: по приходу того же WM_QUIT бесполезно в обработчике некоего окна вечно ждать некоего специального сообщения, надо тупо освобождать ресурсы и выходить.

А это автору процесса решать, что нужно делать.
Никто не мешает ждать более одного сообщения из разных каналов. Это если не маяться дурью с акторами.
А в случае с акторами ты описал selective receive.

V>Поэтому каждую такую последовательность можно выделить в независимый алгоритм-продолжение, дергающийся в произвольные моменты, то бишь асинхронно извне, но синхронно внутри себя.

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

V>Но это событийная модель, а модель синхронного разворота наподобие yield в C# или аналогичных по-сути каналов сингулярити менее гибка, хотя пойдет для несложных сценариев (повторяюсь уже).

Модель сингулярити является строгим надмножеством акторов.

V>И опять же, корректное освобождение ресурсов продолжений в случае простого подвисания ...

И ты опять завис на деталях реализации.
Леса не видишь.

V>Гы-гы, это звучит как аналогичное для стандартного ООП: "нет никакой проблемы соединять объекты друг с другом, просто через вызовы методов, установки св-в или через аргументы конструктора". Так для чего же тогда нужны DI-контейнеры? Особенно там, где SRP во главе угла?

Так они и не нужны.
Это костыль для ООП.
... << RSDN@Home 1.2.0 alpha 4 rev. 1472>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[6]: Actor-based. Success story
От: WolfHound  
Дата: 28.06.12 15:41
Оценка:
Здравствуйте, Mamut, Вы писали:

M>Тут Wolfhound загоняется насчет selective receive, и я даже знаю, какую ссылку он держит в загашнике. Только вот он работает уже 20 лет в промышленных масштабах,

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

M>а Singularity с каналами не видно просто тупо нигде.

Пошла демагогия.

M>Но, естественно, именно это является труъ подходом.

Это подход с точки зрения модели системы является более устойчивым к всяким бякам.
В частности он не сваливается в O(N^2) на ровном месте.

M>Наверное, это тоже в тему: http://cl.ly/111R0x2R343X2d1R0739

Это намек на то, что я не способен писать продакшен код?
... << RSDN@Home 1.2.0 alpha 4 rev. 1472>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[4]: Actor-based. Success story
От: vdimas Россия  
Дата: 28.06.12 16:07
Оценка:
Здравствуйте, Lazin, Вы писали:


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


Да, поэтому рядом я написал:

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


Этот момент как-то не на поверхности, но его быстро обнаруживаешь. Но на уровне конкретного модуля, который не успевает разгребать личные сообщения, затем уже можно делать диагностику. И кстати, если очередь построена на интрузивном способе, без дополнительных узлов-носилетей логики списка, какой смысл ограничивать ее длину? Если сообщение УЖЕ создано, (а оно обязательно создано ПЕРЕД отсылкой) то прилепить ее в конец такой очереди ничего не стоит.


========================
Например, если речь об асинхронном логировании при максимальном уровне логгирования. Тогда надо как-то изворачиваться, например, ренедрить лог-сообщения в буфер в памяти предварительно, т.е., чтобы кол-во операций IO не соответствовало 1:1 к кол-ву сообщений логирования, а записывать их пачками с неким коэф (брали порядка тысячи). Заметно снижает нагрузку на систему в целом. И вообще, вот этот способ передачи "пачками", даже когда речь о передаче сообщений м/у потоками — он самый эффективный, бо постоянное обращение к АПИ ОС или даже просто interlocked-операциям в одном месте снижает эффективность обращения к совсем другому АПИ ОС в другом месте. Например, за одну операцию на lock-free очереди разгребающий поток может получить все имеющиеся сообщения на данный момент (просто голову списка, обнулив shared-переменную через CAS), и потом разбирать пачку сообщений в цикле из локальной переменной-курсора без каких-либо тяжеловесных interlocked-операций. Если примененаа фильтрация этой пачки сообщений, то результат фильтрации чанка сообщений так же лучше прилепить в другую очередь целиком через одну interlocked операцию, вместо "ленивой" фильтрации и посылки резльтата в такую очередь по 1-му сообщению.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.