日常踩坑之gen_server的不同方式处理stop消息
希望将正在运行的gen_server停止的时候,可以通过向gen_server发送stop消息来停止gen_server,有以下几种方式:
1.gen_server:call(Pid,stop):通过gen_server的API,可以看到,该方式可以通过返回{stop,Reason,State}和{stop,Reason,Reply,State}两种返回方式,这两种方式存在一定的差异,如下:
handle_call({stop, 1}, _From, State) -> {stop, normal, "stoped", State}; handle_call({stop, 2}, _From, State) -> {stop, normal, State};
运行测试代码:
(test@192.168.101.13)4> test_stop:start_link(). {ok,<0.90.0>} (test@192.168.101.13)5> test_stop:stop(call1). "stoped" (test@192.168.101.13)6> test_stop:start_link(). {ok,<0.93.0>} (test@192.168.101.13)7> test_stop:stop(call2). ** exception exit: {normal,{gen_server,call,[<0.93.0>,{stop,2}]}} in function gen_server:call/2 (gen_server.erl, line 215)
结论:4元组的返回,会把第三个元素作为call的返回值返回给调用方,而3元组的返回,则会报一个exit的错误,从而导致没有正常返回,所以,如果希望调用方能收到正常返回而不是一个报错,那么就乖乖用4元组的返回方式(别偷懒).
2.gen_server:cast(Pid,stop):cast回调函数只会返回{stop,Reason,State}来停止gen_server,而且,返回值只有ok,(从gen_server.erl中可以看出无论cast处理什么消息都只返回ok).
handle_cast(stop, State) ->
{stop, normal, State};
测试结果:
(test@192.168.101.13)8> test_stop:start_link(). {ok,<0.97.0>} (test@192.168.101.13)9> test_stop:stop(cast). ok (test@192.168.101.13)10> test_stop:stop(cast). no server is exist ok
3.Pid ! stop: 通过gen_server的handle_info回调来停止gen_server,返回{stop,Reason,State}
handle_info(stop_info, State) ->
{stop, normal, State};
测试结果:
(test@192.168.101.13)11> test_stop:start_link(). {ok,<0.101.0>} (test@192.168.101.13)12> test_stop:stop(info). stop_info
注意:此处最终的返回打印是stop_info,即:发给gen_server的停止消息.
最后附上测试的代码:
%%% 停止 stop(Msg) -> case whereis(?MODULE) of Pid when is_pid(Pid) -> handle_msg_(Pid, Msg); undefined -> io:format("no server is exist~n") end. %%测试不同消息 handle_msg_(Pid, call1) -> gen_server:call(Pid, {stop, 1}); handle_msg_(Pid, call2) -> gen_server:call(Pid, {stop, 2}); handle_msg_(Pid, cast) -> gen_server:cast(Pid, stop); handle_msg_(Pid, _) -> Pid ! stop_info.
总结:在使用gen_server:call(pid,stop)时,要特别注意调用方是否需要返回结果!!!,2,3两种方式正常返回即可.