第9章 并发编程中的错误处理
1. 链接:链接定义了一种在两个进程之间的传播路径。如果两个进程被链接在一起,如果其中一个进程消亡,那么系统就会向另一个进程发送一个退出信号。我们把一群与某个给定的进程进行外国投资的进程集合称为该进程的链接集。
链接通过在一个进程中调用link(Pid)来完成,Pid是另一个进程的ID。
2. 退出信号:当一个进程消亡时,它会产生一个叫做退出信号的东西。系统会向这个进程的所有链接进程发送这个退出信号。
退出信号不是一个消息,它是一个信号。退出信号包含一个参数来描述进程消亡的原因,可以通过调用exit(Reason),也可以与系统自动设置。
当一个进程成功的完成spawn所指定的函数而退出时,Why就是normal。
3. 系统进程:当一个进程接收到一个非正常的退出信号时它自己也会消亡,除非它是那种特殊类型的进程-系统进程。当Pid进程向一个系统进程发送一个内容Why的退出信号时,系统会把退出信号转换为消息{'EXIT', Pid, Why},然后送入到系统进程邮箱。
可以通过在一个进程中调用process_flag(trap_exit, true)来将这个进程转换为一个系统进程。
4. 装死:在进程Pid1中调用exit(Pid2,X),这时Pid1会向Pid2发送一个退出信号,但Pid1并没有退出。
5. 进程收到退出信号后的处理关系:
是否捕获 收到的退出信号 收到信号后的动作
是否是系统进程)
true kill 消亡,向链接集广播退出信号killed
true X 将{'EXIT', Pid, X}消息加入到邮箱
false normal 继续运行,不做任何事
false kill 消亡,向链接集广播退出信号killed
false X 消亡,向链接集广播退出信号X
6. kill退出信号可以杀死任何进程,而不是向它发送消息。
exit(Pid, kill)会向进程Pid发送kill退出信号
而exit(kill)只会向链接进程发送原因为kill的退出消息,而不是kill退出信号。
7. on_exit处理程序
on_exit(Pid, Fun) ->
spawn(fun() ->
process_flag(trap_exit, true),
link(Pid),
receive
{'EXIT',Pid,Why} -> Fun(Why)
end
end).
8. 捕获退出的编程模式
模式一:我不在乎创建的进程是否崩溃
创建一个并行进程,当被生成的进程崩溃时,当前进程不会察觉
Pid = spawn(Fun).
模式二:如果我创建的进程非正常的崩溃,我也消亡
%%不要在这之前设置退出信号捕获状态
Pid = spawn_link(Fun).
模式三:如果我创建的进程崩溃,我需要处理错误
...
process_flag(trap_exit, true),
Pid = spawn_link(Fun),
...
loop(...).
loop(State) ->
receive
{'EXIT', SomePid, Reason} ->
%% do somethin with the error
loop(State);
...
end
9. erlang:is_process_alive(Pid) -> true|false,查看进程Pid是否还活着。
10. 错误处理原语
@spec spawn_link(Fun) -> Pid %% 创建进程并链接它们
@spec process_flag(trap_exit, true) %% 把一个普通进程转换为系统进程,可以再把标志设为false而取消对退出信号的处理
@spec link(Pid) -> true %% link是对称的。如果Pid不在在,会抛出noproc异常,如果已经链接,系统会忽略这个调用(不会返回false)
@spec unlink(Pid) -> true %% 移除先前的link
@spec exit(Why) -> none() %% 如果不在catch之内的话,会向当前进程的链接集广播退出信号
@spec exit(Pid, Why) -> true
@spec erlang:monitor(process, Item) -> MonitorRef %% 建立一个监视器,其中Item是一个Pid或者是一个注册的原子
监视器是非对称的,A监视B时,B退出时,A会收到退出信号,而A退出时,B不会收到退出信号。
11. 存活进程:消亡后会被自动重启
keep_alive(Name, Fun) ->
register(Name, Pid=spawn(Fun)),
on_exit(Pid, fun(_Why) -> keep_alive(Name, Fun) end).
这个代码中存在竞争性条件,有问题。