如何用消息回复 gen_server?

How to make a gen_server reply with a message?

我有如下所示的 gen_server。它在大多数情况下都有效。但是,当我从 shell 启动它时,回复会立即返回到 shell 提示符。我原以为它们会作为消息发送回 shells pid,然后我会使用 flush() 来查看它们。

我需要更改什么才能让 foo_worker 以消息形式发送回复?

-module(foo_worker).
-behaviour(gen_server).

%% API
-export([start_link/1, start/1, init/1, send/3, die/1]).
-export([handle_call/3, handle_cast/2, handle_info/2, terminate/2]).

%%%-------------------------------------------------------------------

send(Worker, Ref, Counter) ->
  gen_server:call(Worker, {inc, Ref, Counter}).

die(Worker) ->
  gen_server:cast(Worker, die).

%%%-------------------------------------------------------------------

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

start(Limit) ->
  gen_server:start(?MODULE, [Limit], []).

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

handle_call(_, _, Limit) when Limit =< 0 ->
  exit({worker, eol});
handle_call({inc, Ref, Data}, From, Limit) ->
  io:format("From ~p~n", [From]),
  {reply, {Ref, updated, Data+1}, Limit - 1}.

handle_cast(die, _) ->
  io:format("~p Dying ~n",[self()]),
  exit(normal).

handle_info(Info, State) ->
  io:format("Unkown message ~p for state ~p~n", [Info, State]).

terminate(Reason, State) ->
  io:format("~p Died because ~p with state ~p~n", [self(), Reason, State]).

gen_server:call/2,3 的重点是将消息传递到 gen_server 进程并接收其回复包装到函数调用中。如果您只想处理消息,请不要使用 gen_server:call/2,3 而是让调用者调用 gen_server:cast/2 并在消息中包含调用者 pid:

send(Worker, Ref, Counter) ->
    gen_server:cast(Worker, {inc, Ref, Counter, self()}).

然后让 gen_server:handle_cast/2 理解该消息并使用 pid 将回复发送回调用者:

handle_cast({inc, Ref, Data, From}, Limit) ->
    From ! {Ref, updated, Data+1},
    {noreply, Limit-1}.

顺便提醒一下,当你选择这种方式时,你需要处理可能的失败。如果您将一条消息传递给 gen_server 进程,但它在向您发送回复之前就死了,您需要确保调用者不会坐下来永远等待永远不会到达的回复。执行此操作的最佳方法是使用 monitor — you can have the caller monitor the gen_server process before sending it a message and demonitor it once it receives the reply. If the gen_server process dies, the caller will get a DOWN message instead (see the monitor documentation 了解详细信息)。另请注意,通过这样做,您正在重新实现 gen_server:call/2,3 已经为您完成的一系列工作。