Как “становится” реализованным на языках, которые поддерживают модель агента?

Модель агента приятно описана Агой Gul на его техническом отчете, "Агенты: модель параллельного вычисления в распределенных системах".

На странице 49 он объясняет команду, которой "становятся":

become <expression>

После вызова "становятся X", агент передаст все его сообщения к почтовому ящику (X) другого агента.

Я не уверен, однако, как это реализовано (это, это реализовано вообще) на языках как Erlang и Scala. Это - что-то, что я должен кодировать вручную? Что относительно эффективности? Ага показывает реализацию стека с помощью передачи сообщений. Каждый раз, когда поп или нажатие выполняются, еще одна передающая ссылка добавляется к некоторому агенту... После сотен тысяч операций я ожидал бы, что такая реализация потратит сообщения передачи слишком большого количества времени и не выполнение фактической работы, если некоторая хорошая оптимизация не была выполнена под капотом.

Таким образом, мой вопрос: как передает (или "станьте"), реализованный на типичных языках агента как Erlang, Scala (и библиотеки для других языков)?

12
задан Jay 24 July 2010 в 13:35
поделиться

5 ответов

См. Страницу 12 (страница 26 имеющейся у меня копии в формате PDF) статьи Аги «Действующие лица: модель параллельных вычислений в распределенной системе». "стать" - его актерский язык - это то, как вы указываете № 2, новое поведение для актера. Пересылка сообщений другому субъекту - лишь одно из многих возможных новых вариантов поведения.

Я думаю, что с актерами Scala вы, по сути, находитесь в той же лодке, что и с Erlang, если вам нужно поведение пересылки. Под капотом Scala "react" и "reactWithin" работают так же, как и стали, потому что частичная функция, определенная блоком response, является новым поведением актера, но я не уверен, что подобие даже намеренно.

Большинство (все?) Реализаций «актеров» довольно существенно отклоняются от модели акторов Хьюитта и актерского языка Аги. IIRC часть языка, определяющая поведение актеров на языке Аги, даже не является полной по Тьюрингу. Язык в целом становится полным по Тьюрингу только тогда, когда вы рассматриваете конфигурацию акторов. Я бы сказал, что отношения между моделью акторов и текущими структурами акторов подобны отношениям объектной ориентации в SmallTalk к объектной ориентации в C ++. Есть некоторая передача концепции и похожие термины, но в деталях они очень, очень разные.

3
ответ дан 2 December 2019 в 07:20
поделиться

Это напрямую не реализовано в Erlang, но вы можете написать тривиальную функцию стать , которая получает сообщение, перенаправляет его другому процессу и затем вызывает себя:

become(Pid) ->
    receive
        Msg -> Pid ! Msg
    end,
    become(Pid).

(Промышленная версия

Вызов стать (Pid) фактически превратит вызывающий процесс в процесс Pid с точки зрения внешнего мира.

Это не решает проблемы, которые вы указали, поскольку повторные вызовы становятся , вызывая рост цепочек переадресации. Однако в Erlang этого обычно не происходит, и я не уверен, как процессы Erlang отображаются на модель Actor.

7
ответ дан 2 December 2019 в 07:20
поделиться

Переход на Erlang здесь.

На базовом уровне доступны два варианта. Если вы хотите использовать стать только для изменения поведения данного процесса (см. Пункт 2 списка в разделе 2.1.3), то это просто вопрос вызова следующего цикла с другим рекурсивная функция:

loop(State) ->
     receive
          {normal, Msg} -> loop(State);
          {change, NewLoop} -> NewLoop(State)
     end.

Предполагая, что NewLoop является функцией более высокого порядка, всякий раз, когда вы отправляете сообщение {change, NewLoop} процессу, первоначально выполняющему функцию loop / 1 , тогда он будет использовать NewLoop в качестве своего определения.

Второй вариант - это тот, где вы хотите, чтобы процесс действовал как прокси (и изменял поведение). Это похоже на то, что предлагал Марсело Кантос. Просто создайте цикл процесса и пересылайте сообщения новому (воруя его код):

become(Pid) ->
    receive
        Msg -> Pid ! Msg
    end,
    become(Pid).

Теоретически это делает то, о чем просила статья. Однако на практике использование второго варианта в реальной системе Erlang сопряжено с риском. При обмене данными между двумя процессами часто используется концепция «пометить» сообщение идентификатором процесса отправителя, а ответ будет помечен идентификатором процесса получателя.Может быть произведен обмен следующими сообщениями (это не код, просто обозначение руки):

A = <0.42.0> <-- process identifier
B = <0.54.0>,
A: {A, "hello"},
B: {B, "hi"},
A: {A, "how are you?"}.
B: {B, "Fine!"}.

Итак, когда A ожидает сообщение от B , он сможет соответствовать только для них с помощью шаблона, такого как {B, Message} . В случае перенаправленного сообщения эта схема адресации становится недействительной и просто нарушается.

Альтернативой может быть использование ссылок ( make_ref () ) в качестве схемы адресации для сопоставления сообщений поверх возвращаемых идентификаторов PID. Это разделит использование Pid в качестве адреса и идентификатора с помощью двух разных сущностей.

Есть еще одна проблема, даже если адресация безопасна: зависимости процессов. Что происходит с именованными процессами, сбойными процессами, мониторами и т. Д.? В клиентских процессах могут быть мониторы, ссылки и многое другое, настроенное, чтобы убедиться, что ничего не пойдет не так без уведомления. Путем модификации процесса маршрутизации для перехвата сигналов выхода и их пересылки, должно быть возможно сделать вещи более безопасными:

loop(State) ->
     receive
          {normal, Msg} -> loop(State);
          {become, Pid} ->
              process_flag(trap_exit, true), % catch exit signals as messages
              link(Pid),                     % make it so if one process crashes, the other receives the signal
              become(Pid)
     end.

become(Pid) ->
    receive
        {'EXIT', Pid, killed} -> exit(self(), kill);  %% uncatchable termination
        {'EXIT', Pid, normal} -> ok;                  %% die normally too
        {'EXIT', Pid, Reason} -> exit(Reason);        %% die for the same reason as Pid
        Msg -> Pid ! Msg                              %% forward the message
    end,
    become(Pid).

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

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


* * Вызов функции становится / 1 в последнем примере, скорее всего, должен быть ? МОДУЛЬ: стать (Pid) , чтобы избежать потенциальных сбоев, связанных с загрузкой горячего кода в будущем. *

4
ответ дан 2 December 2019 в 07:20
поделиться

Акторы Akka имеют концепцию "HotSwap", где вы можете послать новую PartialFunction актору, которая заменит его существующий обработчик сообщений. Предыдущий обработчик запоминается и может быть восстановлен. Подробности ищите по слову "hotswap" на http://doc.akkasource.org/actors.

3
ответ дан 2 December 2019 в 07:20
поделиться

Actor является контравариантным кофунктором, поэтому "become" - это просто comap.

Говоря иначе, Actor на сообщениях типа T - это, по сути, функция типа (T => Unit). А это просто композиция функций (с функцией тождества, возможно).

Это реализовано в Scalaz:

val a1 = actor(a => println(a))
val a2 = a1.comap(f)

Актор a2 применяет f к своим сообщениям и затем отправляет результат в a1.

4
ответ дан 2 December 2019 в 07:20
поделиться
Другие вопросы по тегам:

Похожие вопросы: