[erlang 002]gen_server中何时会跑到terminate函数
一、从start方法产出的独立gen_server进程
实验代码:
%%%--------------------------------------
%%% @Module :
%%% @Author :
%%% @Email :
%%% @Created :
%%% @Description:
%%%--------------------------------------
-module(ter_a).
-behaviour(gen_server).
%% gen_server callbacks exports
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
%% gen_server api exports
-export([start/1, link/1, is_alive/0, mistake/0, stop/1]).
-record(state, {}).
%%====================================================================
%% API
%%====================================================================
start(Flag) ->
gen_server:start({local, ?MODULE}, ?MODULE, [Flag], []).
link(Flag) ->
gen_server:call(?MODULE, {link, Flag}).
is_alive() ->
gen_server:call(?MODULE, is_alive).
mistake() ->
gen_server:cast(?MODULE, mistake).
stop(Reason) ->
gen_server:cast(?MODULE, {stop, Reason}).
%%====================================================================
%% gen_server callbacks
%%====================================================================
init([Flag]) ->
case Flag of
0 -> skip;
_ -> process_flag(trap_exit, true)
end,
{ok, #state{}}.
handle_call({link, Flag}, _From, State) ->
{ok, Pid} = ter_b:start_link(Flag),
io:format("handle_call, link to ter_b, LinkPid:~p~n", [Pid]),
{reply, Pid, State};
handle_call(is_alive, _From, State) ->
io:format("yes, i'm alive~n"),
{reply, alive, State};
handle_call(Request, _From, State) ->
io:format("handle_call, Request:~p~n", [Request]),
Reply = ok,
{reply, Reply, State}.
handle_cast(mistake, State) ->
A = 0,
_B = 1 / A,
{noreply, State};
handle_cast({stop, Reason}, State) ->
io:format("handle_cast, stop, Reason:~p~n", [Reason]),
{stop, Reason, State};
handle_cast(Msg, State) ->
io:format("handle_cast, Msg:~p~n", [Msg]),
{noreply, State}.
handle_info(Info, State) ->
io:format("handle_info, Info:~p~n", [Info]),
{noreply, State}.
terminate(Reason, _State) ->
io:format("terminate, Reason:~p~n", [Reason]),
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
1) 无论有没有在初始化的时候捕捉退出即process_flag(trap_exit, true),只要回调函数以{stop, Reason, State}或者{stop, Reason, State}结束,则都会跑到terminate/2。特别的如果Reson为normal、shutdown或者{shutdown, ElseInfo},进程会正常退出,否则进程会报错然后才退出。部分实验数据如下:
1> ter_a:start(0).
{ok,<0.33.0>}
2> ter_a:stop(normal).
handle_cast, stop, Reason:normal
terminate, Reason:normal
ok
3> ter_a:start(1).
{ok,<0.36.0>}
4> ter_a:stop(else).
handle_cast, stop, Reason:else
terminate, Reason:else
ok
5>
=ERROR REPORT==== 28-Apr-2015::10:38:42 ===
** Generic server ter_a terminating
** Last message in was {'$gen_cast',{stop,else}}
** When Server state == {state}
** Reason for termination ==
** else
实际上,可以通过gen_server的源码了解到这些东西:
dispatch({'$gen_cast', Msg}, Mod, State) ->
Mod:handle_cast(Msg, State);
dispatch(Info, Mod, State) ->
Mod:handle_info(Info, State). handle_msg({'$gen_call', From, Msg}, Parent, Name, State, Mod) ->
case catch Mod:handle_call(Msg, From, State) of
...
{stop, Reason, Reply, NState} ->
{'EXIT', R} =
(catch terminate(Reason, Name, Msg, Mod, NState, [])),
reply(From, Reply),
exit(R);
Other -> handle_common_reply(Other, Parent, Name, Msg, Mod, State)
end;
handle_msg(Msg, Parent, Name, State, Mod) ->
Reply = (catch dispatch(Msg, Mod, State)),
handle_common_reply(Reply, Parent, Name, Msg, Mod, State). handle_msg({'$gen_call', From, Msg}, Parent, Name, State, Mod, Debug) ->
case catch Mod:handle_call(Msg, From, State) of
...
{stop, Reason, Reply, NState} ->
{'EXIT', R} =
(catch terminate(Reason, Name, Msg, Mod, NState, Debug)),
_ = reply(Name, From, Reply, NState, Debug),
exit(R);
Other ->
handle_common_reply(Other, Parent, Name, Msg, Mod, State, Debug)
end;
handle_msg(Msg, Parent, Name, State, Mod, Debug) ->
Reply = (catch dispatch(Msg, Mod, State)),
handle_common_reply(Reply, Parent, Name, Msg, Mod, State, Debug). handle_common_reply(Reply, Parent, Name, Msg, Mod, State) ->
case Reply of
...
{stop, Reason, NState} ->
terminate(Reason, Name, Msg, Mod, NState, []);
{'EXIT', What} ->
terminate(What, Name, Msg, Mod, State, []);
_ ->
terminate({bad_return_value, Reply}, Name, Msg, Mod, State, [])
end. handle_common_reply(Reply, Parent, Name, Msg, Mod, State, Debug) ->
case Reply of
...
{stop, Reason, NState} ->
terminate(Reason, Name, Msg, Mod, NState, Debug);
{'EXIT', What} ->
terminate(What, Name, Msg, Mod, State, Debug);
_ ->
terminate({bad_return_value, Reply}, Name, Msg, Mod, State, Debug)
end.
terminate(Reason, Name, Msg, Mod, State, Debug) ->
case catch Mod:terminate(Reason, State) of
{'EXIT', R} ->
FmtState = format_status(terminate, Mod, get(), State),
error_info(R, Name, Msg, FmtState, Debug),
exit(R);
_ ->
case Reason of
normal ->
exit(normal);
shutdown ->
exit(shutdown);
{shutdown,_}=Shutdown ->
exit(Shutdown);
_ ->
FmtState = format_status(terminate, Mod, get(), State),
error_info(Reason, Name, Msg, FmtState, Debug),
exit(Reason)
end
end.
2) 从外部用exit(Pid, Reason)去杀死这个进程的情况
a) 只要Reason不是normal或者kill,如果进程没有捕捉退出,则进程会以Reason的理由直接退出,不会跑到terminate/2;如果有捕捉退出,进程不会退出,而且这个操作会转换成一条法外消息{'EXIT', From, Reason}发到进程,由handle_info处理:
1> {ok, Pid} = ter_a:start(0).
{ok,<0.33.0>}
2> exit(Pid, else).
true
3> ter_a:is_alive().
** exception exit: {noproc,{gen_server,call,[ter_a,is_alive]}}
in function gen_server:call/2 (gen_server.erl, line 182)
4> {ok, Pid1} = ter_a:start(1).
{ok,<0.38.0>}
5> exit(Pid1, else).
handle_info, Info:{'EXIT',<0.36.0>,else}
true
6> ter_a:is_alive().
yes, i'm alive
alive
b) Reason是normal,如果进程没有捕捉退出,则进程不会退出;如果有捕捉退出,进程也不会退出,只会转为{'EXIT', From, normal}消息投递到Info的消息{'EXIT', From, normal}消息投递到进程信箱,由handle_info处理:
1> {ok, Pid0} = ter_a:start(0).
{ok,<0.33.0>}
2> exit(Pid0, normal).
true
3> ter_a:is_alive().
yes, i'm alive
alive
4> ter_a:stop(normal).
handle_cast, stop, Reason:normal
terminate, Reason:normal
ok
5> {ok, Pid1} = ter_a:start(1).
{ok,<0.38.0>}
6> exit(Pid1, normal).
handle_info, Info:{'EXIT',<0.31.0>,normal}
true
7> ter_a:is_alive().
yes, i'm alive
alive
c) Reason是kill,无论进程有没有捕捉退出,进程都会无条件退出,而且不会跑到terminate/2:
1> {ok, Pid0} = ter_a:start(0).
{ok,<0.33.0>}
2> exit(Pid0, kill).
true
3> ter_a:is_alive().
** exception exit: {noproc,{gen_server,call,[ter_a,is_alive]}}
in function gen_server:call/2 (gen_server.erl, line 182)
4> {ok, Pid1} = ter_a:start(1).
{ok,<0.38.0>}
5> exit(Pid1, kill).
true
6> ter_a:is_alive().
** exception exit: {noproc,{gen_server,call,[ter_a,is_alive]}}
in function gen_server:call/2 (gen_server.erl, line 182)
二、gen_server进程相互链接的情况(假如有进程A、B)
进程B的代码如下:
%%%--------------------------------------
%%% @Module :
%%% @Author :
%%% @Email :
%%% @Created :
%%% @Description:
%%%--------------------------------------
-module(ter_b).
-behaviour(gen_server). %% gen_server callbacks exports
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]). %% gen_server api exports
-export([start_link/1, is_alive/0]).
-record(state, {}). %%====================================================================
%% API
%%====================================================================
start_link(Flag) ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [Flag], []). is_alive() ->
gen_server:call(?MODULE, is_alive). %%====================================================================
%% gen_server callbacks
%%==================================================================== init([Flag]) ->
case Flag of
0 -> skip;
_ ->
process_flag(trap_exit, true)
end,
{ok, #state{}}. handle_call(is_alive, _From, State) ->
io:format("ter_b, i'm alive~n"),
{reply, alive, State}; handle_call(_Request, _From, State) ->
io:format("ter_b, handle_call, _Request:~p~n", [_Request]),
Reply = ok,
{reply, Reply, State}. handle_cast(_Msg, State) ->
io:format("ter_b, handle_cast, Msg:~p~n", [_Msg]),
{noreply, State}. handle_info(_Info, State) ->
io:format(ter_b, "handle_info, Msg:~p~n", [_Info]),
{noreply, State}. terminate(_Reason, _State) ->
io:format("ter_b, terminate~n"),
ok. code_change(_OldVsn, State, _Extra) ->
{ok, State}.
1) B不捕捉退出,如果A异常退出,则B也会随之退出,A进程会跑到terminate/2而B进程不会;如果A正常退出,则B不做任何处理:
异常退出:
1> ter_a:start(0).
{ok,<0.33.0>}
2> ter_a:link(0).
handle_call, link to ter_b, LinkPid:<0.35.0>
<0.35.0>
3> ter_a:mistake().
terminate, Reason:{badarith,
[{ter_a,handle_cast,2,[{file,"ter_a.erl"},{line,63}]},
{gen_server,handle_msg,5,
[{file,"gen_server.erl"},{line,599}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,237}]}]}
ok
4>
=ERROR REPORT==== 28-Apr-2015::11:35:25 ===
** Generic server ter_a terminating
** Last message in was {'$gen_cast',mistake}
** When Server state == {state}
** Reason for termination ==
** {badarith,[{ter_a,handle_cast,2,[{file,"ter_a.erl"},{line,63}]},
{gen_server,handle_msg,5,[{file,"gen_server.erl"},{line,599}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,237}]}]} 4> ter_a:is_alive().
** exception exit: {noproc,{gen_server,call,[ter_a,is_alive]}}
in function gen_server:call/2 (gen_server.erl, line 182)
5> ter_b:is_alive().
** exception exit: {noproc,{gen_server,call,[ter_b,is_alive]}}
in function gen_server:call/2 (gen_server.erl, line 182)
正常退出:
1> ter_a:start(0).
{ok,<0.33.0>}
2> ter_a:link(0).
handle_call, link to ter_b, LinkPid:<0.35.0>
<0.35.0>
3> ter_a:stop(normal).
handle_cast, stop, Reason:normal
terminate, Reason:normal
ok
4> ter_a:is_alive().
** exception exit: {noproc,{gen_server,call,[ter_a,is_alive]}}
in function gen_server:call/2 (gen_server.erl, line 182)
5> ter_b:is_alive().
ter_b, i'm alive
alive
2) B捕捉退出,只要A退出,B都会随着退出,两个进程都会跑到terminate/2:
异常退出:
Eshell V6.2 (abort with ^G)
1> ter_a:start(0).
{ok,<0.33.0>}
2> ter_a:link(1).
handle_call, link to ter_b, LinkPid:<0.35.0>
<0.35.0>
3> ter_a:mistake().
terminate, Reason:{badarith,
[{ter_a,handle_cast,2,[{file,"ter_a.erl"},{line,63}]},
{gen_server,handle_msg,5,
[{file,"gen_server.erl"},{line,599}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,237}]}]}
ter_b, terminate
ok
4>
=ERROR REPORT==== 28-Apr-2015::11:40:11 ===
** Generic server ter_a terminating
** Last message in was {'$gen_cast',mistake}
** When Server state == {state}
** Reason for termination ==
** {badarith,[{ter_a,handle_cast,2,[{file,"ter_a.erl"},{line,63}]},
{gen_server,handle_msg,5,[{file,"gen_server.erl"},{line,599}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,237}]}]} =ERROR REPORT==== 28-Apr-2015::11:40:11 ===
** Generic server ter_b terminating
** Last message in was {'EXIT',<0.33.0>,
{badarith,
[{ter_a,handle_cast,2,
[{file,"ter_a.erl"},{line,63}]},
{gen_server,handle_msg,5,
[{file,"gen_server.erl"},{line,599}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,237}]}]}}
** When Server state == {state}
** Reason for termination ==
** {badarith,[{ter_a,handle_cast,2,[{file,"ter_a.erl"},{line,63}]},
{gen_server,handle_msg,5,[{file,"gen_server.erl"},{line,599}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,237}]}]} 4> ter_a:is_alive().
** exception exit: {noproc,{gen_server,call,[ter_a,is_alive]}}
in function gen_server:call/2 (gen_server.erl, line 182)
5> ter_b:is_alive().
** exception exit: {noproc,{gen_server,call,[ter_b,is_alive]}}
in function gen_server:call/2 (gen_server.erl, line 182)
正常退出:
1> ter_a:start(0).
{ok,<0.33.0>}
2> ter_a:link(1).
handle_call, link to ter_b, LinkPid:<0.35.0>
<0.35.0>
3> ter_a:stop(normal).
handle_cast, stop, Reason:normal
terminate, Reason:normal
ok
ter_b, terminate
三、一些结论和补充
1) 在gen_server中,进程结束时不是什么情况都会跑到terminat/2函数;
2) 如果有两个gen_server进程相互链接,需要让两个进程同时存在同时消亡(无论原因),并且在消亡的时候都保证要跑到terminate/2去,则需要给每个进程捕获退出消息process_flag(trap_exit, true);
3) 如果gen_server进程是supervision tree的一部分,并且由supervision去终止,只要符合以下条件terminate/2就会以shutdown作为Reason被调用
a) gen_server进程捕获退出;
b) 在supervision关于这个gen_server子策略中,Shutdown的值是一个整数,而非brutal_kill .
(完)
[erlang 002]gen_server中何时会跑到terminate函数的更多相关文章
- Erlang模块gen_server翻译
gen_server 概要: 通用服务器行为描述: 行为模块实现服务器的客户端-服务器关系.一个通用的服务器进程使用这个模块将实现一组标准的接口功能,包括跟踪和错误报告功能.它也符合OTP进程监控树. ...
- Erlang的gen_server的terminate()/2未执行
官方资料参考: Module:terminate(Reason, State) Types: Reason = normal | shutdown | {shutdown,term()} | term ...
- 转载: 让我们聊聊Erlang的nif中资源的安全释放
让我们聊聊Erlang的nif中资源的安全释放 http://my.oschina.net/u/236698/blog/479221
- 专访探探DBA张文升:PG在互联网应用中同样也跑的很欢畅
张文升认为,PG无论在可靠性和性能方面都不输其它任何关系型数据库 张文升,探探DBA,负责探探的数据库架构.运维和调优的工作.拥有8年开发经验,曾任去哪儿网DBA. 9月24日,张文升将参加在 ...
- C语言学习笔记 (002) - C++中引用和指针的区别(转载)
下面用通俗易懂的话来概述一下: 指针-对于一个类型T,T*就是指向T的指针类型,也即一个T*类型的变量能够保存一个T对象的地址,而类型T是可以加一些限定词的,如const.volatile等等.见下图 ...
- hive中的lateral view 与 explode函数的使用
hive中的lateral view 与 explode函数的使用 背景介绍: explode与lateral view在关系型数据库中本身是不该出现的. 因为他的出现本身就是在操作不满足第一范式的数 ...
- MATLAB中白噪声的WGN和AWGN函数的使用
MATLAB中白噪声的WGN和AWGN函数的使用如下: MATLAB中产生高斯白噪声非常方便,可以直接应用两个函数,一个是WGN,另一个是AWGN.WGN用于产生高斯白噪声,AWGN则用于在某一 信号 ...
- FastReport调用Delphi中的人民币大写转换自定义函数
FastReport调用Delphi中的人民币大写转换自定义函数 FastReport调用Delphi中的人民币大写转换自定义函数 function TJzpzEdit1.MoneyCn(mmje ...
- Matlab中如何将(自定义)函数作为参数传递给另一个函数
假如我们编写了一个积分通用程序,想使它更具有通用性,那么可以把被积函数也作为一个参数.在c/c++中,可以使用函数指针来实现上边的功能,在matlab中如何实现呢?使用函数句柄--这时类似于函数指针的 ...
随机推荐
- [Oracle] CPU/PSU补丁安装详细教程
Oracle CPU的全称是Critical Patch Update, Oracle对于其产品每个季度发行一次安全补丁包,通常是为了修复产品中的安全隐患,以下是对CPU/PSU补丁安装的具体操作步骤 ...
- bean作用域
https://blog.csdn.net/u011468990/article/details/49995865 其中比较常用的是singleton和prototype两种作用域.对于singlet ...
- redis+php微博功能的redis数据结构设计总结(四)
概述: 1.完全采用redis作为数据库实现微博的登录2.发布3.微博的显示4.实现整个功能使用了redis的string,list,hashes四个数据类型,以及string类型的数值自增功能 一. ...
- Js、jquery学习笔记
end() 方法 重新定位到上次操作的元素,一般与siblings()一起使用,操作其兄弟元素.如:$(this).addClass("highlight").children(& ...
- 将socket程序从linux移植到windows上
今天突然想试下纯socket编程在两个系统上代码重合量有多大,只要不使用VC自定义的宏(比如SOCKET.SOCKADDR等等)感觉代码重合量挺大的. 比如最简单的TCP客户端和服务端对话,在VC中用 ...
- sklearn中的predict与predict_proba的区别(得到各条记录每个标签的概率(支持度))
假定在一个k分类问题中,测试集中共有n个样本.则: predict返回的是一个大小为n的一维数组,一维数组中的第i个值为模型预测第i个预测样本的标签: predict_proba返回的是一个n行k列的 ...
- POJ 2549 Sumsets(折半枚举+二分)
Sumsets Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 11946 Accepted: 3299 Descript ...
- zookeeper的四种类型的节点
znode创建类型(CreateMode),有以下四种: PERSISTENT 持久化节点 PERSISTENT_SEQUENTIAL 顺序自动编号持久化节点,这种节点会根据当前已存在的节点数自动加 ...
- 第八章 搭建hadoop2.2.0集群,Zookeeper集群和hbase-0.98.0-hadoop2-bin.tar.gz集群
安装配置jdk,SSH 一.首先,先搭建三台小集群,虚拟机的话,创建三个 下面为这三台机器分别分配IP地址及相应的角色:集群有个特点,三台机子用户名最好一致,要不你就创建一个组,把这些用户放到组里面去 ...
- 【学习笔记】Markdown入门
Markdown入门 最近把博客园的编辑器换成了Markdown. 语法入门 Markdown确实好用,本来我想总结一下常用Markdown的语法,但有下面这篇文章在,我实在是不敢画蛇添足了. 基 ...