Какой смысл Spawn(Node, Fun) на erlang, если Node должен иметь тот же модуль, который загружается как клиентский узел?
Зачем создавать иллюзию, что вы отправляете Fun на удаленный узел для выполнения в новом процессе? Если клиентский узел должен иметь такой же загружаемый модуль с Fun, определенным как узел сервера в любом случае. Почему бы не только порождать (Node, M, F, A), что дает понять, что вы отправляете определение вызова функции, а не саму Fun.
2 ответа
Давайте рассмотрим два возможных случая
Функции, относящиеся к функциям модуля
Fun = fun file:getcwd/0,
erlang:spawn(Node, Fun).
В этом случае Fun
действительно должен быть загружаемым на удаленной стороне.
Анонимные функции
Fun = fun() -> io:format("My node is ~p~n", [node()]) end,
erlang:spawn(Node, Fun).
Они также могут быть вызваны.
подведение
Хотя есть некоторые ограничения, налагаемые на функцию, которая может быть удалена, удаленно, эта форма все еще подходит и может быть отброшена, потому что тогда второй случай станет невозможным
Также
Некоторые заблуждения могут пойти из этой статьи
На самом деле, если вы запускаете erlang:fun_info
для анонимной функции вы увидите, что она обеспечивает реализацию в форме AST
(b@lol4t0-home)21> rp(erlang:fun_info(fun() -> io:format("My node is ~p~n", [node()]) end)).
[{pid,<0.96.0>},
{module,erl_eval},
{new_index,20},
{new_uniq,<<99,62,121,82,122,95,246,237,63,72,118,40,4,
25,16,50>>},
{index,20},
{uniq,52032458},
{name,'-expr/5-fun-3-'},
{arity,0},
{env,[{[],
{eval,#Fun<shell.21.31625193>},
{value,#Fun<shell.5.31625193>},
[{clause,1,[],[],
[{call,1,
{remote,1,{atom,1,io},{atom,1,format}},
[{string,1,"My node is ~p~n"},
{cons,1,{call,1,{atom,1,node},[]},{nil,1}}]}]}]}]},
{type,local}]
Пример ниже показывает, что удаленный узел может выполнить код, который никогда не был загружен на него, даже вызов анонимной функции, которая была определена на узле вызывающего:
на узле Тити
Erlang/OTP 19 [erts-8.0] [64-bit] [smp:4:4] [async-threads:10]
Eshell V8.0 (abort with ^G)
(titi@XXXXXXXX)1> net_adm:ping(toto@XXXXXXXX).
pong
(titi@XXXXXXXX)2> F = fun F(X) -> receive {plus,N} -> F(X+N); get -> io:format("state is ~p~n",[X]), F(X); stop -> bye end end.
#Fun<erl_eval.30.52032458>
(titi@XXXXXXXX)3> G = fun() -> register(server,self()), F(0) end.
#Fun<erl_eval.20.52032458>
(titi@XXXXXXXX)4> spawn(toto@XXXXXXXX,G).
<7039.67.0>
state is 0
state is 5
state is 10
(titi@XXXXXXXX)5>
на узле тото
Erlang/OTP 19 [erts-8.0] [64-bit] [smp:4:4] [async-threads:10]
Eshell V8.0 (abort with ^G)
(toto@XXXXXXXX)1> server ! get. % will print state is 0
get
(toto@XXXXXXXX)2> server ! {plus,5}. % new state = 5
{plus,5}
(toto@XXXXXXXX)3> server ! get. % will print state is 5
get
(toto@XXXXXXXX)4> server ! {plus,5}. % new state = 10
{plus,5}
(toto@XXXXXXXX)5> server ! get. % will print state is 10
get
(toto@XXXXXXXX)6> server ! stop. % ends the server process
stop
(toto@XXXXXXXX)7> server ! get. % message will fail
** exception error: bad argument
in operator !/2
called as server ! get
(toto@XXXXXXXX)8>
Я также проверил, что если вы замените код {plus,N} -> F(X+N);
от {plus,N} -> F(maps:put(X,X+N,maps:new()));
который вызывает модуль, не загруженный оболочкой при запуске, он также работает нормально.
редактировать
Прочитайте комментарии, кажется, что делать тест в оболочке не правильно, 2 вывода, хотя:
- не полагайтесь на оболочку только для проверки,
- должен быть способ выполнить на удаленном узле код, который был известен только на узле вызывающего, поскольку оболочка может это сделать...