Почему вы можете создавать несколько ссылок монитора на один и тот же процесс в Erlang?

Вот пример трассировки, где я могу вызвать erlang:monitor/2 для того же Pid:

1> Loop = fun F() -> F() end.
#Fun<erl_eval.30.99386804>
2> Pid = spawn(Loop).
<0.71.0>
3> erlang:monitor(process, Pid).
#Ref<0.2485499597.1470627842.126937>
4> erlang:monitor(process, Pid).
#Ref<0.2485499597.1470627842.126942>
5> erlang:monitor(process, Pid).
#Ref<0.2485499597.1470627842.126947>

Выражения, возвращаемые инструкцией № 4 и № 5, отличаются от выражения № 3, что означает возможность создания нескольких ссылок на монитор между текущим процессом и Pid. Есть ли практический случай, когда вам нужно или использовать несколько ссылок монитора на один и тот же процесс?

Я ожидаю, что это вернет ту же ссылку (возврат новой, возможно, будет означать, что старая не удалась/разбился), следуя той же логике, которая существует для link/1.


person goncalotomas    schedule 22.02.2018    source источник


Ответы (1)


Представьте, что вы используете стороннюю библиотеку, которая делает это (в основном то, что делают функции OTP *:call/*):

call(Pid, Request) ->
    call(Pid, Request, ?DEFAULT_TIMEOUT).

call(Pid, Request, Timeout) ->
    MRef = erlang:monitor(process, Pid),
    Pid ! {call, self(), MRef, Request},
    receive
      {answer, MRef, Result} ->
        erlang:demonitor(Mref, [flush]),
        {ok, Result};
      {'DOWN', MRef, _, _, Info} ->
        {error, Info}
    after Timeout ->
        erlang:demonitor(MRef, [flush]),
        {error, timeout}
    end.

а затем вы используете его в своем коде, где вы будете отслеживать тот же процесс Pid, а затем вызывать функцию call/2,3.

my_fun1(Service) ->
    MRef = erlang:monitor(process, Service),
    ok = check_if_service_runs(MRef),
    my_fun2(Service),
    mind_my_stuf(),
    ok = check_if_service_runs(MRef),
    erlang:demonitor(MRef, [flush]),
    return_some_result().

check_if_service_runs(MRef) ->
    receive
      {'DOWN', MRef, _, _, Info} -> {down, Info}
    after 0 -> ok
    end.

my_fun2(S) -> my_fun3(S).

% and a many layers of other stuff and modules
my_fun3(S) -> call(S, hello).

Каким неприятным сюрпризом было бы, если бы erlang:monitor/2,3 всегда возвращала одну и ту же ссылку, а erlang:demonitor/1,2 удаляла ваш предыдущий монитор. Это было бы источником уродливых и неразрешимых ошибок. Вы должны начать думать, что есть библиотеки, другие процессы, ваш код — это часть огромной системы, а Erlang сделан опытными людьми, которые все продумали. Здесь важна ремонтопригодность.

person Hynek -Pichi- Vychodil    schedule 22.02.2018
comment
Начав изучать OTP, должен признаться, что это не пришло мне в голову. Однако не будет ли cast более подходящим примером, чем вызов, поскольку он асинхронный и может иметь несколько ожидающих запросов? Я думаю, что процесс, который выполняет вашу реализацию call/3, может выполнять только одну операцию за раз, поэтому требуется только одна ссылка на мониторинг. Это правда? В остальном, думаю, я точно понял, чем это может быть полезно. Большое спасибо за ответ. - person goncalotomas; 22.02.2018
comment
@goncalotomas: Ты все еще не понял. Проблема не в том, является ли это вызовом или приведением, а в коде вне их. Вы можете сохранить монитор в своем коде или в другой сторонней библиотеке. В функциональном языке с неизменностью принято создавать библиотеки с непрозрачными состояниями, обратными вызовами и т. д., и эти состояния могут содержать MRef и хранить их столько, сколько им нужно. Вы должны мыслить масштабнее. Существует крупнейший коммерческий проект функционального языка ADX301, для которого был разработан и использован Erlang с 2 миллионами строк кода. Вы должны мыслить намного шире. - person Hynek -Pichi- Vychodil; 22.02.2018
comment
@goncalotomas: я добавил еще немного кода, который, надеюсь, поможет вам лучше понять проблему. Это очень искусственно, но я надеюсь, что это поможет. - person Hynek -Pichi- Vychodil; 22.02.2018
comment
Я думаю, что теперь я это понимаю - может быть несколько «слоев» кода, который self() заканчивает выполнением, который может вызывать erlang:monitor/2 для одного и того же Pid, и в этом случае вам очень нужна уникальная ссылка, иначе все довольно быстро станет хаотичным. Я думаю, это объясняет это, спасибо! Ваши последние дополнения кода очень помогли. - person goncalotomas; 23.02.2018