4 erlang shutdown termination exit terminate
我读过《Learn Some Erlang》中有关主管的内容,但完全迷失了。它们具有停止和终止功能
每当您想要终止应用程序时,您都可以关闭虚拟机的最高管理程序(这是通过 init:stop/1 等函数为您完成的)。然后该主管要求其每个子级终止。如果一些孩子是主管,他们也会这样做:
这似乎发送关闭消息以接收“退出”确认
因此,调用stop来关闭进程。但是,在本文后面,他们说必须调用退出函数(一个新的水果!)
当顶级管理程序被要求终止时,它会对每个 Pids 调用 exit(ChildPid, shutdown)。如果子进程是一个工作者并且捕获退出,它将调用自己的终止函数。不然的话,就只能死了。当主管收到关闭信号时,它会以同样的方式将其转发给自己的子级。
最后,他们在子模块中定义了stop函数
-module(musicians).
-behaviour(gen_server).
-export([start_link/2, stop/1]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, code_change/3, terminate/2]).
stop(Role) -> gen_server:call(Role, stop).
Run Code Online (Sandbox Code Playgroud)
init:stop定义在哪里?
他们还发送停止消息
handle_call(stop, _From, S=#state{}) -> {stop, normal, ok, S};
Run Code Online (Sandbox Code Playgroud)
他们的handle_info
handle_info(timeout, S = #state{name=N, skill=bad}) ->
case random:uniform(5) of
1 -> io:format("~s played a false note. Uh oh~n",[N]),
{stop, bad_note, S};
_ -> io:format("~s produced sound!~n",[N]),
{noreply, S, ?DELAY}
end;
Run Code Online (Sandbox Code Playgroud)
揭示了其回复和终止之间的联系
terminate(normal, S) ->
io:format("~s left the room (~s)~n",[S#state.name, S#state.role]);
terminate(bad_note, S) ->
io:format("~s sucks! kicked that member out of the band! (~s)~n",
[S#state.name, S#state.role]);
terminate(shutdown, S) ->
io:format("The manager is mad and fired the whole band! "
"~s just got back to playing in the subway~n", [S#state.name]);
Run Code Online (Sandbox Code Playgroud)
然而,这一切看起来一团糟。你能把这些东西绑在一起吗?
init:stop()
stop()
表示调用模块中的函数init
。这告诉 Erlang 系统很好地关闭所有正在运行的应用程序,然后退出。这就像关闭操作系统一样。application:stop(AppName)
然后将为每个正在运行的应用程序执行该调用。这些将依次告诉应用程序的顶级主管关闭。
“关闭信号”是指从主管到孩子的信号。如果主管想要关闭其下面的所有内容,它会将这些信号发送给其子级。
“退出信号”是进程终止(停止或崩溃)时向与其链接的任何其他进程发出的信号。监督程序与其子级链接,并且在向子级发送关闭信号后,它将等待它们的退出信号 ( 'EXIT'
) 以确保它们已终止。
当子进程(通常使用像 gen_server 这样的 OTP 行为实现)从上面获取关闭信号时,它首先调用自己的terminate(...)
回调函数,您可以在其中执行任何必要的清理 - 它就像 OOP 中的析构函数。
stop(Role)
上面的函数只是gen_server
一个辅助函数,提供一个 API 来要求该服务器很好地停止(不涉及主管)。您可以gen_server:call(Role, stop)
直接使用,但您必须知道内部实现也使用原子stop
作为停止消息。将此类详细信息隐藏在 API 后面是一种很好的做法。
最后,内置函数(BIF)的exit(Pid, shutdown)
命名很糟糕,因为它所做的只是向另一个进程发送信号,并不影响执行调用的进程。将其视为send_exit_signal(Pid, shutdown)
. 它不应该与 BIF 混淆,BIFexit(Info)
终止执行它的进程(通过引发异常)。这两个 BIF 都属于该erlang
模块。
另外,在这样的非正式文本中,信号可能被称为“消息”,但事实并非如此——它不会最终进入接收者的邮箱,除非接收者“捕获退出”(trap_exit
设置了进程标志)。普通消息实际上是一种特殊的信号,总是发送到邮箱;退出信号(除了normal
)也会导致接收器死亡,除非它们捕获退出,以便可以将链接的进程组一起关闭。