在Joe Armstrong的 Programming Erlang 第12章“创建一组一起消亡的进程”中,给出了以下代码:
% (Some variables are renamed and comments added for extra clarity)
start(WorkerFuns) ->
spawn(fun() ->
% Parent process
[spawn_link(WorkerFun) || WorkerFun <- WorkerFuns],
receive
after infinity -> true
end
end).
由此产生的过程相互关联,如下所示:
+- parent -+
/ | \
/ | \
worker1 worker2 .. workerN
如果一个工作进程崩溃,则父进程崩溃,然后其余的工作进程也崩溃。但是,如果所有工作进程正常退出,则父进程将永远存在,尽管处于挂起状态。
虽然Erlang进程应该是廉价的,但是如果在一个长时间运行的服务中多次调用start/1
,那么每次所有工作者正常退出时,一个进程(父进程)就会出现“泄漏”。
这在实践中是否曾经是个问题?当所有工作者正常退出时,正确考虑的额外代码(见下文)是否值得?
start(WorkerFuns) ->
spawn(fun() ->
% Parent process
process_flag(trap_exit, true),
[spawn_link(WorkerFun) || WorkerFun <- WorkerFuns],
parent_loop(length(WorkerFuns))
end).
parent_loop(0) ->
% All workers exited normally
true;
parent_loop(RemainingWorkers) ->
receive
{'EXIT', _WorkerPid, normal} ->
parent_loop(RemainingWorkers - 1);
{'EXIT', _WorkerPid, CrashReason} ->
exit(CrashReason)
end.
1条答案
按热度按时间txu3uszq1#
您的分析是正确的。给定的代码没有考虑到工作进程的正常终止,并将留下一个悬空进程。每次调用的空间泄漏约为2kb,因此在大型系统中,除非您调用
start/1
一千次或更多次,否则您不太可能注意到它,但对于预期“永远”运行的系统,您绝对应该添加额外的代码。