2009-08-14 6 views
5

EDIT:Как вы можете параметризовать модуль gen_server?

Я не хочу использовать параметры в качестве общего назначения для создания программ Erlang - я все еще изучаю традиционные принципы проектирования. Я также не хочу подражать ООП. Мое единственное дело здесь - сделать мои вызовы gen_server согласованными между экземплярами сервера. Это больше похоже на исправление сломанной абстракции для меня. Я могу представить мир, в котором язык или OTP сделали его удобным для использования api любого gen_server экземпляра, и это мир, в котором я хочу жить.

Спасибо Zed за то, что я показал, что моя основная цель возможна.

Может ли кто-нибудь выяснить способ использования параметризованных модулей в gen_servers? В следующем примере предположим, что test_child является gen_server с одним параметром. Когда я пытаюсь запустить его, все это я получаю:

42> {test_child, "hello"}:start_link(). 
** exception exit: undef 
    in function test_child:init/1 
     called as test_child:init([]) 
    in call from gen_server:init_it/6 
    in call from proc_lib:init_p_do_apply/3 

В конечном счете, я пытаюсь выяснить способ использовать несколько именованных экземпляров gen_server. Насколько я могу судить, как только вы начнете это делать, вы больше не сможете использовать свой симпатичный API и бросать сообщения в своих экземплярах с помощью gen_server: call и gen_server: cast. Если бы я мог указать экземплярам свои имена, эту проблему можно было бы смягчить.

+0

Вы хотите, чтобы несколько gen_servers использовали один и тот же модуль обратного вызова, каждый с другим именем? Или несколько gen_servers с тем же именем? – Jacob

+0

Несколько генераторов gen_servers, использующих один и тот же модуль обратного вызова. Вы можете передать start_link имя для регистрации разных «экземпляров» с помощью. Когда у вас есть один экземпляр, кажется, что это типично, чтобы дать ему то же имя, что и модуль, а затем ваш открытый API работает так: some_module: some_function(). ... но это похоже только удобство. Если вы зарегистрируете gen_server с другим именем, он больше не работает. Я хотел бы получить что-то вроде: 1> Mod: some_function(). 2> Mod1: some_function(). ... где каждая переменная относится к разным экземплярам модуля gen_server – mwt

+0

Или, в качестве альтернативы, я хотел бы знать, почему я не должен заботиться об этом. Каждое вступление gen_server, которое я видел, устанавливает API, а не просто использует вызовы/вызовы gen_server.Будучи новым для Erlang, я ожидал, что вы сможете легко клонировать многие процессы, и я удивлен, что функция API ломается, как только вы переименуете gen_servers. – mwt

ответ

-4
-module(zed, [Name]). 
-behavior(gen_server). 

-export([start_link/0, init/1, handle_cast/2]). 
-export([increment/0]). 

increment() -> 
    gen_server:cast(Name, increment). 

start_link() -> 
    gen_server:start_link({local, Name}, {?MODULE, Name}, [], []). 

init([]) -> 
    {ok, 0}. 

handle_cast(increment, Counter) -> 
    NewCounter = Counter + 1, 
    io:format("~p~n", [NewCounter]), 
    {noreply, NewCounter}. 

Этот модуль работает отлично для меня:

Eshell V5.7.2 (abort with ^G) 
1> S1 = zed:new(s1). 
{zed,s1} 
2> S1:start_link(). 
{ok,<0.36.0>} 
3> S1:increment(). 
1 
ok 
4> S1:increment(). 
2 
ok 
+0

Спасибо. Я собираюсь добавить это в свой проект и посмотреть, превышают ли преимущества возражения, поднятые в этой теме. – mwt

+1

Я думаю, что отрицательные голоса в этом ответе должны прояснить :) – gleber

+0

Думаю, мы уже выяснили, что вы и архаллус не в пользу параметризованных модулей, но спасибо за то, что у вас есть счет :) – Zed

3

Я думаю, что вы не должны использовать эту функцию, таким образом. Похоже, вы идете после OO-подобного интерфейса к вашим gen_servers. Вы используете локально зарегистрированные имена для этой цели - добавьте много общего состояния в вашу программу, которая равна The Bad Thing. Только важные и центральные серверы должны быть зарегистрированы с register BIF - пусть все остальные будут неназванными и управляться каким-то менеджером поверх них (который, вероятно, должен быть зарегистрирован под каким-то именем).

+0

В чем разница между «менеджером поверх них» и встроенным реестром процессов с точки зрения функциональности? – Zed

+1

Разница заключается в том, что менеджер - это специализированный процесс, который делает одно за раз. В то время как встроенный реестр процессов является реестром общего назначения для центральных серверов, к которому следует обращаться глобально (на узле). Вероятность столкновения имен намного выше при использовании реестра процессов. Кроме того, реестр процесса AFAIK не предназначен для хранения большого количества элементов, то есть предназначен для небольшого количества зарегистрированных процессов. – gleber

+0

. Я согласен с вами, и я вижу сходство с рамками внедрения инъекций в мире OO. Вместо использования глобальных статических заводских методов используется инъекция зависимости, она увеличивает изоляцию и модульность и упрощает тестирование. Даже критически важные и центральные серверы не должны ссылаться на жестко закодированные имена атомов, вместо этого эти атомы должны передаваться на сервер gen в качестве параметра. Параметризированные модули были бы когда-то подходными, если бы у них не было проблем, о которых я расскажу. Вот почему я всегда использую обычное явное состояние. Имена жесткого кодирования предназначены для поведения супервизора. – Christian

10

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

-module(test_module, [Param1]). 

some_method() -> Param1. 

эквивалентно

-module(test_non_paramatized_module). 

some_method(Param1) -> Param1. 

Бывший не покупает вам много на всех, и очень мало существующий Erlang код использует этот стиль.

Обычно принято передавать аргумент имени (при условии, что вы создаете несколько подобных gen_servers, зарегистрированных под разными именами) для функции start_link.

start_link(Name) -> gen_server:start_link({local, Name}, ?MODULE, [Name], []). 

Вторая часть для ответа заключается в том, что gen_server совместим с paramatized модулями:

-module(some_module, [Param1, Param2]). 

start_link() -> 
    PModule = ?MODULE:new(Param1, Param2), 
    gen_server:start_link(PModule, [], []). 

Param1 и Param2 будут доступны во всех функциях gen_server обратного вызова.

Как отмечает Zed, поскольку start_link принадлежит к paramatized модуля, вам нужно будет сделать следующее для того, чтобы назвать его:

Instance = some_module:new(Param1, Param2), 
Instance:start_link(). 

Я считаю, что это особенно некрасиво стиль - код, вызывающий some_module:new/n должен знать количество и порядок параметров модуля. Код, который вызывает some_module:new/n, также не может находиться в some_module. Это, в свою очередь, затрудняет горячее обновление, если число или порядок параметров модуля изменяются. Вам нужно будет координировать загрузку двух модулей вместо одного (some_module и его интерфейса/конструктора), даже если бы вы могли найти способ обновления кода some_module. В незначительной заметке этот стиль несколько усложняет grep-код для использования some_module:start_link.


Рекомендуемый способ передать параметры gen_servers явно с помощью gen_server:start_link/3,4 аргументов функции и сохранять их в состоянии значения вы вернетесь из ?MODULE:init/1 callack.

-module(good_style). 

-record(state, {param1, param2}). 

start_link(Param1, Param2) -> 
    gen_server:start_link(?MODULE, [Param1, Param2], []). 

init([Param1, Param2]) -> 
    {ok, #state{param1=Param1,param2=Param2}}. 

Используя этот стиль означает, что вы не будете пойманы различными частями OTP, которые еще не в полной мере поддерживают paramatized модули (новая и еще экспериментальная функция). Кроме того, значение состояния может быть изменено во время работы экземпляра gen_server, но параметры модуля не могут.

Этот стиль также поддерживает горячее обновление с помощью механизма изменения кода. Когда вызывается функция code_change/3, вы можете вернуть новое значение состояния. Не существует соответствующего способа вернуть новый экземпляр параматизированного модуля в код gen_server.

+0

Я не вижу смысла вашей второй части. В параметризованных модулях нет «статических» функций (я тоже их пропускаю). Вы не сможете вызвать start_link() без предварительного вызова some_module: new/2. – Zed

+0

Конечно, у вас может быть отдельный модуль, например some_module_factory, который имеет ваш start_link(), возвращающий новый параметризованный some_module, но затем мы слишком глубоко вписываем код в стиле o ... – Zed

+2

Я полагаю, вы могли бы создать интерфейсный модуль (который был параметризованный), который передается с непараметризированным gen_server через gen_server: call/2. Это позволило бы избежать проблемы обновления с gen_server и означать, что вы передаете ссылку на сервер по-разному (как параматизированный модуль, а не параметр функции). Вы все равно останетесь с проблемой обновления координационных двух модулей, и я не могу рекомендовать это как хороший стиль. – archaelus

10

Я просто хочу сказать две вещи:

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

  • никогда, НИКОГДА, НИКОГДА не , НИКОГДА не использовать форму, которую вы пытаетесь! Это осталось от старых дней, которые никогда не имели в виду то, что вы намеревались, и теперь сильно устарели.

+0

@mwt, пожалуйста, рассмотрите пункты Роберта. Он один из создателей Эрланг, и он, безусловно, тот, кого должен слушать каждый Эрланг-новичок! – gleber

+0

Хорошо, спасибо, что сообщили мне. Я обращу особое внимание на любые комментарии, которые он делает.:) Что касается первого пункта, порядок комментариев может смутить. Я не могу сказать, ссылается ли он на архалов «последний или второй, чтобы продолжить редактирование на данный момент, но я думаю, что второе и последнее. Если это так, это похоже на пример Зеда. Что касается пункта 2, я никогда не собирался действительно использовать этот стиль. – mwt

+0

Я имел в виду последний способ, который он показал, тот, который не использует параметризованные модули, модуль good_style. Archaelus также комментирует, что при использовании параметризованных модулей он вполне подходит. Ну, даже если вы никогда не намеревались использовать стиль в своем примере, который является стилем, который вы использовали, поэтому нужно предположить, что это то, что вы намеревались. – rvirding

Смежные вопросы