- 链接进程
- 创建和删除链接
- 创建和删除链接
链接进程
进程可以互相监视。这里要引入两个概念,进程链接和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 **