В вызове gen_server:reply(From, Msg)
, From
- это не просто клиент: на самом деле это кортеж, содержащий два значения: идентификатор процесса вызывающей стороны и уникальную ссылку.Мы можем видеть это в реализации gen_server:reply/2
:
%% -----------------------------------------------------------------
%% Send a reply to the client.
%% -----------------------------------------------------------------
reply({To, Tag}, Reply) ->
catch To ! {Tag, Reply}.
Идея состоит в том, что Tag
является уникальным значением, предоставляемым вызывающим, так что вызывающий может различитьрезультат этого вызова из любого другого входящего сообщения:
Ref = make_ref(),
MyServer ! {'$gen_call', {self(), Ref}, foo},
receive
{Ref, Reply} -> io:format("Result of foo call: ~p~n", [Reply])
end
В приведенном выше коде receive
будет блокироваться, пока не получит ответ на этот самый вызов.
(gen_server:call/2
выполняет действия, описанные выше, и дополнительно контролирует сервер в случае его сбоя и проверяет тайм-ауты.)
Причина, по которой это недокументировано, заключается в том, что он считается внутренней деталью реализации, подлежащей изменению, и пользователям рекомендуетсяполагаться на gen_server:call
и gen_server:reply
вместо генерации и сопоставления самих сообщений.
Большую часть времени вам вообще не нужно будет использовать gen_server:reply/2
: процесс сервера получаетВызовите и обрабатывает его синхронно, возвращая reply
кортеж:
handle_call(foo, _From, State) ->
%% ignoring 'From' here, because we're replying immediately
{reply, foo_result, State}.
Но иногда вы хотите, чтобы процесс сервера задерживал ответ на вызов, например, ожидание сетиork input:
handle_call(foo, From, State) ->
send_request(foo),
NewState = State#state{pending_request = From},
{noreply, NewState}.
handle_info({received_response, Response}, State = #state{pending_request = From}) ->
gen_server:reply(From, Response),
NewState = State#state{pending_request = undefined},
{noreply, NewState}.
В приведенном выше примере мы сохраняем значение From
в состоянии сервера, а когда ответ приходит в виде сообщения Erlang, мы направляем его вызывающей стороне, которая блокируетпока не получит ответ.(Более реалистичный пример будет обрабатывать несколько запросов одновременно и каким-то образом сопоставлять входящие ответы с невыполненными запросами.)