为什么我无法终止Erlang进程?

时间:2019-09-08 18:41:08

标签: process erlang exit

我正在生成2个进程,看来我无法杀死其中两个进程:

  • restarter-每当worker发生故障时产生的过程
  • worker-从shell获取消息,将它们连接起来并在退出的reason中将它们返回到重启器的过程,该重启器又将它们转发到shell。

worker进程无法终止,因为restarter会在任何陷阱退出消息上重新启动它。但是什么使restarter进程保持活力?

-module(mon).
-compile_flags([debug_info]).
-export([worker/1,init/0,restarter/2,clean/1]).

% ctrl+g
init()->
    Pid=spawn(?MODULE,restarter,[self(),[]]),
    register(restarter,Pid),
    Pid.


restarter(Shell,Queue)->
    process_flag(trap_exit,true),

    Wk=spawn_link(?MODULE,worker,[Queue]),
    register(worker,Wk),

    receive

        {'EXIT',Pid,{Queue,normal}}->Shell ! {Queue,"From res: worker died peacefully, wont restart"};

        {'EXIT',Pid,{Queue,horrible}} ->
                Shell ! {Queue,"Processed so far:"},
            Shell ! "will restart in 5 seconds, select fresh/stale -> 1/0",
            receive
                1 -> 
                    Shell ! "Will restart fresh",
                    restarter(Shell,[]);
                0 ->Shell ! "Will continue work",
                    restarter(Shell,Queue)
            after 5000 ->
              Shell ! "No response -> started with 666",
              restarter(Shell,[666]) 
            end;
        {MSG}->Shell ! {"Unknown message...closing",MSG}
end.


worker(Queue)->

    receive 
        die->exit({Queue,horrible});
        finish->exit({Queue,normal});
        MSG->worker([{time(),MSG}|Queue])
    end.

用法

mon:init().
regs().  %worker and restarter are working
whereis(worker) ! "msg 1", whereis(worker) ! "msg2".
whereis(worker) ! finish.
flush().  % should get the first clause from restarter 
regs().  % worker should be up and running again
exit(whereis(restarter),reason).
regs().  % restarter should be dead

1 个答案:

答案 0 :(得分:3)

在这种情况下,restarter进程正在捕获出口,因此exit(whereis(restarter), reason)不会杀死它。退出信号被转换为消息,并被放入进程的消息队列中:

> process_info(whereis(restarter), messages).
{messages,[{'EXIT',<0.76.0>,reason}]}

它仍在消息队列中的原因是receive表达式中没有任何子句与此消息匹配。前两个子句特定于worker进程使用的退出原因,最后一个子句看起来像是一个包罗万象的子句,但实际上并非如此-它匹配具有一个元素的元组的任何消息。如果将其写为MSG而不是{MSG},则它将收到退出原因消息,并向外壳发送“未知消息”。

如果您真的想终止该进程,请使用kill原因:

exit(whereis(restarter), kill).

kill退出信号是不可捕获的,即使进程正在捕获退出。


另一件事:只有当工作队列为空时,前两个接收子句才会匹配。那是因为它重用了变量名Queue,所以{'EXIT',Pid,{Queue,normal}}中的队列必须等于作为参数传递给restarter函数的值。在这种情况下,通常会在接收子句中使用NewQueue或其他变量。