我的gen_server包含这样的方法:
handle_call(error, From, State) -> io:format("Inside the handle_call error~n"), 1/0.
它提供start
(非start_link
)功能:
start() -> gen_server:start({local, ?MODULE}, ?MODULE, [], []). normal_call() -> gen_server:call(?MODULE, {normal}). error_call() -> gen_server:call(?MODULE, error).
我start
从shell 调用函数:
c(some_module). some_module:start().
然后我调用终止服务器进程的错误调用,因为除零错误,但它也终止了shell(调用进程).我不明白为什么?它们没有链接但仍然使用新的pid重新启动shell.这是gen_server的预期行为,还是我做错了什么?
更新:它仍然无法正常工作,以帮助我发布完整的代码.
-module(some_test). -behaviour(gen_server). -compile(export_all). %% api functions, can be directly used by the user start() -> gen_server:start({local, ?MODULE}, ?MODULE, [], []). normal_call() -> gen_server:call(?MODULE, normal, infinity). error_call() -> gen_server:call(?MODULE, error, infinity). %% service specific function shoule not be call directly init(_) -> io:format("Inside the init ( ~p )~n", [self()]), io:format("Inside the init...~n"), {ok, nil}. handle_call(normal, _, _) -> io:format("Inside the handle_call normal~n"), {reply, ok, nil}; handle_call(error, _, nil) -> io:format("Inside the handle_call error~n"), 1/0. terminate(Reason, _) -> io:format("Inside the terminate~p~n", [Reason]). %% just to complete handle_info(_, _) -> {noreply, nil}. handle_cast(_, _) -> {noreply, nil}. code_change(_, _, _) -> {ok, nil}. %% a single test function that prove that called process was removed immeditely, did not wait for 5 seconds test_function() -> io:format("Id is : ~p~n", [self()]), ?MODULE:start(), ?MODULE:normal_call(), ?MODULE:error_call(), io:format("Id is : ~p~n", [self()]). %% never reached :(
并开始我使用这个: -
c(some_test). some_test:test_function().
得到了输出: -
20> some_test:test_function(). Id is : <0.84.0> Inside the init ( <0.92.0> ) Inside the init... Inside the handle_call normal Inside the handle_call error Inside the terminate{badarith, [{some_test,handle_call,3, [{file,"some_test.erl"},{line,29}]}, {gen_server,try_handle_call,4, [{file,"gen_server.erl"},{line,629}]}, {gen_server,handle_msg,5, [{file,"gen_server.erl"},{line,661}]}, {proc_lib,init_p_do_apply,3, [{file,"proc_lib.erl"},{line,240}]}]} =ERROR REPORT==== 3-Dec-2015::18:36:22 === ** Generic server some_test terminating ** Last message in was error ** When Server state == nil ** Reason for termination == ** {badarith,[{some_test,handle_call,3,[{file,"some_test.erl"},{line,29 {gen_server,try_handle_call,4, [{file,"gen_server.erl"},{line,629}]}, {gen_server,handle_msg,5,[{file,"gen_server.erl"},{line,6 {proc_lib,init_p_do_apply,3, [{file,"proc_lib.erl"},{line,240}]}]} ** exception exit: {{badarith, [{some_test,handle_call,3, [{file,"some_test.erl"},{line,29}]}, {gen_server,try_handle_call,4, [{file,"gen_server.erl"},{line,629}]}, {gen_server,handle_msg,5, [{file,"gen_server.erl"},{line,661}]}, {proc_lib,init_p_do_apply,3, [{file,"proc_lib.erl"},{line,240}]}]}, {gen_server,call,[some_test,error,infinity]}} in function gen_server:call/3 (gen_server.erl, line 212) in call from some_test:test_function/0 (some_test.erl, line 51) 21>
所以我们可以看到some_test之后的最后一行:error_call()永远不会被调用?因为调用进程也终止了?
gen_server:call/2,3是一个同步消息传递函数.这意味着在gen_server向发件人发送回复之前它不会返回.在这种情况下,gen_server崩溃,因此永远不会发送响应.结果是调用函数超时,并且该超时的结果是崩溃.
您会注意到被叫方立即崩溃,但是呼叫者在5秒后崩溃.那是因为默认超时是5000毫秒(检查上面链接的文档).尝试将其设置为infinity
- 您的调用进程将挂起,永远阻止等待永不响应的响应.
[ 更新:直接来自shell的调用会立即崩溃,因为在执行过程中会引发异常.这与超时到期不同 - 两种情况都会导致崩溃,但异常是即时的.]
解决这个问题的方法是使用gen_server:cast/2发送异步消息.尝试定义这个:
handle_cast(error, State) -> io:format("Inside the handle_cast error~n"), 1/0.
这只会导致被叫者崩溃; 呼叫者将在消息离开的瞬间继续.它就像扔棒球而走开,而不是投掷飞旋镖和等待.
当您获得使用Erlang的经验时,您将倾向于使用尽可能多的cast
s 编写代码,并且仅call
在情况确实需要同步消息时才使用s (某些状态值实际上取决于事件的顺序).这使得系统在许多方面更松散地耦合,并使您的调用函数能够抵抗它们发送数据的过程中的失败.
编辑
shell崩溃是因为它在执行期间进行了调用崩溃.但是,使用异步消息不会发生这种情况.我添加了另一个模块并扩展了你的模块以说明这一点.现在有一个名为的新模块some_tester
会中继我们发送的所有内容,因此它会崩溃,而不是shell.以下是相关位:
-module(some_tester). -compile(export_all). start() -> gen_server:start({local, ?MODULE}, ?MODULE, [], []). relay(Nature, Message) -> gen_server:cast(?MODULE, {Nature, Message}). init(State) -> ok = z("Starting..."), {ok, State}. handle_call(Message, From, S) -> ok = z("Received ~tp from ~tp", [Message, From]), {reply, ok, S}. handle_cast({cast, Message}, S) -> ok = some_test:cast(Message), {noreply, S}; handle_cast({call, Message}, S) -> ok = z("Sending call with ~tp", [Message]), Reply = some_test:normal_call(Message), ok = z("Received ~tp as reply", [Reply]), {noreply, S}; handle_cast({infinite, Message}, S) -> ok = z("Sending infinite with ~tp", [Message]), Reply = some_test:infinite_call(Message), ok = z("Received ~tp as reply", [Reply]), {noreply, S}; handle_cast(Message, S) -> ok = z("Unexpected ~tp", [Message]), {noreply, S}.
以下是相关部分some_test
:
start() -> gen_server:start({local, ?MODULE}, ?MODULE, [], []). normal_call(Message) -> gen_server:call(?MODULE, Message). infinite_call(Message) -> gen_server:call(?MODULE, Message, infinity). cast(Message) -> gen_server:cast(?MODULE, Message). % ... handle_call(normal, _, S) -> io:format("Inside the handle_call normal~n"), {reply, ok, S}; handle_call(error, _, S) -> io:format("Inside the handle_call error~n"), {reply, 1/0, S}; handle_call(bad_reply, _, S) -> io:format("Inside the handle_call error~n"), foo; handle_call(Message, From, S) -> io:format("Received ~tp from ~tp~n", [Message, From]), {reply, ok, S}. handle_cast(error, S) -> io:format("Bad arith: ~tp~n", [1/0]), {noreply, S}; handle_cast(Message, S) -> io:format("Received ~tp~n", [Message]), {noreply, S}.
这是一个玩弄它的游戏.注意shell自己调用的输出self()
和flush()
:
1> c(some_test). some_test.erl:31: Warning: this expression will fail with a 'badarith' exception some_test.erl:32: Warning: variable 'S' is unused some_test.erl:40: Warning: this expression will fail with a 'badarith' exception {ok,some_test} 2> c(some_tester). {ok,some_tester} 3> {ok, Test} = some_test:start(). Inside the init ( <0.45.0> ) Inside the init... {ok,<0.45.0>} 4> {ok, Tester} = some_tester:start(). <0.47.0> some_tester: Starting... {ok,<0.47.0>} 5> monitor(process, Test). #Ref<0.0.2.178> 6> monitor(process, Tester). #Ref<0.0.2.183> 7> self(). <0.33.0> 8> some_tester:relay(call, foo). <0.47.0> some_tester: Sending call with foo ok Received foo from {<0.47.0>,#Ref<0.0.2.196>} <0.47.0> some_tester: Received ok as reply 9> some_tester:relay(cast, bar). Received bar ok 10> some_tester:relay(call, error). <0.47.0> some_tester: Sending call with error ok Inside the handle_call error Inside the terminate{badarith, [{some_test,handle_call,3, [{file,"some_test.erl"},{line,31}]}, {gen_server,try_handle_call,4, [{file,"gen_server.erl"},{line,629}]}, {gen_server,handle_msg,5, [{file,"gen_server.erl"},{line,661}]}, {proc_lib,init_p_do_apply,3, [{file,"proc_lib.erl"},{line,240}]}]} Inside the terminate{{badarith, [{some_test,handle_call,3, [{file,"some_test.erl"},{line,31}]}, {gen_server,try_handle_call,4, [{file,"gen_server.erl"},{line,629}]}, {gen_server,handle_msg,5, [{file,"gen_server.erl"},{line,661}]}, {proc_lib,init_p_do_apply,3, [{file,"proc_lib.erl"},{line,240}]}]}, {gen_server,call,[some_test,error]}} 11> =ERROR REPORT==== 3-Dec-2015::22:52:17 === ** Generic server some_test terminating ** Last message in was error ** When Server state == nil ** Reason for termination == ** {badarith,[{some_test,handle_call,3,[{file,"some_test.erl"},{line,31}]}, {gen_server,try_handle_call,4, [{file,"gen_server.erl"},{line,629}]}, {gen_server,handle_msg,5,[{file,"gen_server.erl"},{line,661}]}, {proc_lib,init_p_do_apply,3, [{file,"proc_lib.erl"},{line,240}]}]} =ERROR REPORT==== 3-Dec-2015::22:52:17 === ** Generic server some_tester terminating ** Last message in was {'$gen_cast',{call,error}} ** When Server state == [] ** Reason for termination == ** {{{badarith,[{some_test,handle_call,3,[{file,"some_test.erl"},{line,31}]}, {gen_server,try_handle_call,4, [{file,"gen_server.erl"},{line,629}]}, {gen_server,handle_msg,5,[{file,"gen_server.erl"},{line,661}]}, {proc_lib,init_p_do_apply,3, [{file,"proc_lib.erl"},{line,240}]}]}, {gen_server,call,[some_test,error]}}, [{gen_server,call,2,[{file,"gen_server.erl"},{line,204}]}, {some_tester,handle_cast,2,[{file,"some_tester.erl"},{line,24}]}, {gen_server,try_dispatch,4,[{file,"gen_server.erl"},{line,615}]}, {gen_server,handle_msg,5,[{file,"gen_server.erl"},{line,681}]}, {proc_lib,init_p_do_apply,3,[{file,"proc_lib.erl"},{line,240}]}]} 11> self(). <0.33.0> 12> flush(). Shell got {'DOWN',#Ref<0.0.2.178>,process,<0.45.0>, {badarith, [{some_test,handle_call,3, [{file,"some_test.erl"},{line,31}]}, {gen_server,try_handle_call,4, [{file,"gen_server.erl"},{line,629}]}, {gen_server,handle_msg,5, [{file,"gen_server.erl"},{line,661}]}, {proc_lib,init_p_do_apply,3, [{file,"proc_lib.erl"},{line,240}]}]}} Shell got {'DOWN',#Ref<0.0.2.183>,process,<0.47.0>, {{badarith, [{some_test,handle_call,3, [{file,"some_test.erl"},{line,31}]}, {gen_server,try_handle_call,4, [{file,"gen_server.erl"},{line,629}]}, {gen_server,handle_msg,5, [{file,"gen_server.erl"},{line,661}]}, {proc_lib,init_p_do_apply,3, [{file,"proc_lib.erl"},{line,240}]}]}, {gen_server,call,[some_test,error]}}} ok
仔细阅读.