在gen_server进程中使用receive
子句是否可以?我正在阅读设计可伸缩性的第10章,它说:
是否有任何理由让作者这么说?我知道如果我们想要与gen_server进行通信,我们应该gen_server:call/cast
,但是如果在handle_call/cast
部分,我们需要receive
子句的强大功能呢?可以使用吗?
答案 0 :(得分:3)
Pascal's answer告诉您为什么在receive
中使用handle_call
和其他回调可能会或可能不是一个好主意。我只想指出,这似乎不是作者对该句的意思。要点是,如果您期望两条消息A和B,并且您不确定哪一条消息将首先到达,但您想在B之前处理A,则可以使用receive
轻松执行此操作:< / p>
wait_for_a() ->
receive
{a, A} ->
process_a(A),
wait_for_b()
end.
wait_for_b() ->
receive
{b, B} ->
process_b(B)
end.
但是,如果你的进程是gen_server或gen_fsm,你不能做那样的事情:你的回调函数将按顺序调用传入的消息,如果你想推迟消息B以便以后处理,你必须将它保存在您的州或其他地方。
在receive
/ handle_call
中使用handle_cast
时要考虑的另一件事是你可能会收到system messages,理论上你应该准备好处理它。
答案 1 :(得分:2)
默认情况下,gen_server调用(和gen_fsm)的超时时间为5秒。如果回调函数持续时间过长,则服务器崩溃并显示原因
{timeout,{gen_server,call,[GenServerPid,LastMessage]}}
似乎没有强制转换函数具有相同的超时时间(我猜因为回调会立即返回),但如果回调执行阻止它,则会导致下一次调用失败。
所以我认为这不是一个好主意,除非您的应用程序要求在回调期间很快收到消息(换句话说,如果您认为缺少第二条消息是错误条件)
检查以下代码:
-module (tout).
-behaviour(gen_server).
-define(SERVER, ?MODULE).
%% export interfaces
-export([start_link/0,call/2,cast/2]).
%% export callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
%% INTERFACES %%
start_link() ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
call(Pid,Time) ->
gen_server:call(Pid,{wait,Time}).
cast(Pid,Time) ->
gen_server:cast(Pid,{wait,Time}).
%% CALLBACK FUNCTIONS %%
init([]) ->
{ok, #{}}.
handle_call({wait,Time}, _From, State) ->
timer:sleep(Time),
{reply, done, State};
handle_call(_Request, _From, State) ->
{reply, {error, unknown_call}, State}.
handle_cast({wait,Time}, State) ->
timer:sleep(Time),
{noreply, State};
handle_cast(_Msg, State) ->
{noreply, State}.
handle_info(_Info, State) ->
{noreply, State}.
terminate(_Reason, _State) ->
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%% LOCAL FUNCTIONS %%
Shell会话:
1> c(tout).
{ok,tout}
2> tout:start_link().
{ok,<0.136.0>}
3> tout:call(tout,500).
done
4> tout:call(tout,5100).
** exception exit: {timeout,{gen_server,call,[tout,{wait,5100}]}}
in function gen_server:call/2 (gen_server.erl, line 204)
5> tout:start_link().
{ok,<0.141.0>}
6> tout:cast(tout,10000).
ok
7> tout:cast(tout,1000).
ok
8> % wait a little .
8> tout:call(tout,100).
done
9> tout:cast(tout,10000).
ok
10> % no wait .
11> tout:call(tout,100).
** exception exit: {timeout,{gen_server,call,[tout,{wait,100}]}}
in function gen_server:call/2 (gen_server.erl, line 204)
12>
[编辑] 是的gen_fsm无法选择性接收,通常的问题是:
Fsm处于state_1并收到一条应该在state_2中处理的消息,就在要求转到state_2的消息到达之前:
这种情况无法通过其中一个回调中的接收块解决,因为此问题出现在2个回调之间,而进程正在执行gen_fsm代码。
我认为应该通过R19中的新行为gen_statem解决这个问题之一(尽管我读得很快)