如何使用消息进行gen_server回复?

时间:2015-05-23 14:40:48

标签: erlang

我有如下所示的gen_server。它在很大程度上起作用。但是,当我从shell启动它时,回复会立即回到shell提示符。我希望它们作为消息发送回shell 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]).

1 个答案:

答案 0 :(得分:3)

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 - 您可以让调用者在发送消息之前监视gen_server进程,并在收到回复后监视demonitor进程。如果gen_server进程终止,则调用者将收到DOWN消息(有关详细信息,请参阅the monitor documentation)。另请注意,通过执行此操作,您将重新实现gen_server:call/2,3已为您执行的一系列操作。