链接进程
链接进程
进程可以互相监视。这里要引入两个概念,进程链接 和EXIT信号。在执行期间,进程可以与其他进程(和端口,参见??章节)建立链接。当一个进程终止(无论正常或非正常终止)时,一个特殊的EXIT信号将被发送到所有与即将终止的进程相链接的进程(及端口)。该信号的格式如下:
{'EXIT', Exiting_Process_Id, Reason}
Exiting_Process_Id是即将终止的进程的进程标识,Reason可以是任意的Erlang项式。
收到Reason不是原子式normal的EXIT信号时,信号接收进程的默认动作是立即终止并,同时向当前与之链接的进程发送EXIT信号。默认情况下,Reason为原子式normal的EXIT信号将被忽略。
EXIT信号的默认处理方式行为可以被覆写,以允许进程在接收到EXIT信号时采取任意必要的动作。
创建和删除链接
进程可以链接到其它进程和端口。进程间的链接都是双向的,也就是说,如果进程A链接到进程B,那么进程B也会自动链接到进程A。
通过执行BIF link(Pid)便可创建链接。调用link(Pid)时,若调用进程和Pid之间已经存在链接,则不会产生任何影响。
进程终止时,它所持有的链接都将被删除。也可以通过执行BIF unlink(Pid)显式删除链接。由于所有链接都是双向的,删除这一端到另一端的链接的同时,另一端的到这一端的链接也会被删除。若调用进程和Pid之间原本就没有链接,unlink(Pid)不会产生任何影响。
BIF spawn_link/3在创建新进程的同时还会在调用进程和新进程间建立链接。其行为可以定义为:
spawn_link(Module, Function, ArgumentList) ->
link(Id = spawn(Module, Function, ArgumentList)),
Id.
只不过spawn和link是原子方式执行的。这是为了避免调用进程在执行link之前就被EXIT信号杀死。尝试向一个不存在的进程发起链接将导致信号{'EXIT',Pid,noproc}被发送至link(Pid)的调用进程。
程序7.2中,函数start/1建立了若干以链式互联的进程,其中第一个进程的注册名为start(参见图7.1)。函数test/1向该注册进程发送消息。每个进程不断打印自己在链中的位置及收到的消息。消息stop令链中最后一个进程执行BIF exit(finished),该BIF将导致该进程异常终止。
程序7.2
-module(normal).
-export([start/1, p1/1, test/1]).
start(N) ->
register(start, spawn_link(normal, p1, [N - 1])).
p1(0) ->
top1();
p1(N) ->
top(spawn_link(normal, p1, [N - 1]),N).
top(Next, N) ->
receive
X ->
Next ! X,
io:format("Process ~w received ~w~n", [N,X]),
top(Next,N)
end.
top1() ->
receive
stop ->
io:format("Last process now exiting ~n", []),
exit(finished);
X ->
io:format("Last process received ~w~n", [X]),
top1()
end.
test(Mess) ->
start ! Mess.
我们启动三个进程(参见图7.1(a))
> normal:start(3).
true
图7.1 进程退出信号的传递
然后向第一个进程发送消息123:
> normal:test(123).
Process 2 received 123
Process 1 received 123
Last process received 123
123
再向第一个进程发送消息stop:
> normal:test(stop).
Process 2 received stop
Process 1 received stop
Last process now exiting
stop
这条消息顺着进程链传递下去,我们将看到它最终导致链中最后一个进程的终止。这会引发一个发送给倒数第二个进程的EXIT信号,致其异常终止(图7.1(b)),接着又向第一个进程发送EXIT信号(图7.1(c)),于是注册进程start也异常终止(图 7.1(d))。
若这时再向注册进程start发送一条新消息,将由于目标进程不存在而失败:
> normal:test(456).
!!! Error in process <0.42.1> in function
!!! normal:test(456)
!!! reason badarg
** exited: badarg **
