如何用消息回复 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
已经为您完成的一系列工作。
我有如下所示的 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
已经为您完成的一系列工作。