在Erlang中处理手动滚动监督员的退出信号?

时间:2014-12-06 17:58:27

标签: erlang exit supervisor

我正在尝试为我所制作的流程编写主管。我已经调查了一段时间无济于事,所以希望有人可以提供帮助。

我对我必须使用的接口有一定的限制,因为这是用于分配的,所以我知道使用Lists的示例以及Erlang站点上更详细的OTP示例,但是这些不适合。我提供了从我的应用程序中抽象出来的示例来演示该问题。

我试图在没有正常原因退出时重新启动任意工作程序。工人流程很简单:

-module(my_mod).

-export([start/1, init/1]).

start(Pid)->
  {ok, spawn_link(?MODULE, init, [Pid])}.

init(Pid) ->
  register(Pid, self()),
  io:format("Started ~p~n",[Pid]),
  loop().

loop() ->
  receive stop -> exit(byebye) end.

在主管中我使用ETS选项卡来跟踪工作人员并重新启动它们,主管是这样的:

-module(my_sup).

-export([start_link/0, init/1, add_item/1, remove_item/1]).


start_link() ->
  spawn(?MODULE, init, [self()]).

init(Pid) ->
  process_flag(trap_exit, true),
  register(?MODULE, Pid),
  ets:new(?MODULE, [set, named_table, public]),
  loop().

add_item(Pid) ->
  ets:insert(?MODULE, {Pid}),
  my_mod:start(Pid),
  {ok, Pid}.

remove_item(Pid) ->
  ets:delete(?MODULE, [Pid]).

loop() ->
  io:format("Looping ~n"),
  receive
    {'EXIT', Pid, _Reason} ->
      remove_item(Pid),
      add_item(Pid)
  end.

所以我相信我正在做一些事情,my_mod被链接回主管,以便通知退出信号,主管设置了trap_exit,以便主管有机会处理信号。但是我发现我只是得到一个**异常退出:停止抛出,我不确定为什么会这样?

我的测试用例如下:

1> c(my_sup), c(my_mod), my_sup:start_link().
Looping 
<0.42.0>
2> my_sup:add_item(a). 
Started a
{ok,a}
3> a ! stop .
** exception exit: byebye

有人能指出我正确的方向吗?

2 个答案:

答案 0 :(得分:3)

在你的shell中,你的add_item/1调用发生在shell进程中,而不是在超级用户进程中,这意味着超级用户没有链接到新添加的进程,而是你的shell。在add_item/1中,您应该向管理员进程发送一条消息,告诉它启动一个新的工作者,并更改您的管理程序循环以处理该新消息并从那里启动该工作程序。

答案 1 :(得分:1)

好的,正如Steve V指出的那样,我的问题是我实际上在调用add_item / 1时链接到shell进程而不是主管。我找到了以下解决方案,还有一些问题,如果你试图添加一个现有的Pid东西爆炸,但它是一个适合初始问题的解决方案。 my_mod已更改为以下内容:

-module(my_mod).

-export([start/1, init/1]).

start(Name)->
  {ok, spawn_link(?MODULE, init, [Name])}.

init(Name) ->
  register(Name, self()),
  io:format("Started ~p~n",[Name]),
  loop().

loop() ->
  receive 
    exit -> exit(kill);
    stop -> exit(graceful)
  end. 

并且主管修改为:

-module(my_sup).

-export([start_link/0, init/0, add_item/1, remove_item/1]).

start_link() -> register(?MODULE, spawn(?MODULE, init, [])).

init() ->
  process_flag(trap_exit, true),
  ets:new(?MODULE, [set, named_table, public]), loop().

add_item(Name) -> ?MODULE ! {add_item, Name}.

update_item(Name, Pid) -> ?MODULE ! {update_item, Name, Pid}.

remove_item(Name) -> ?MODULE ! {remove_item, Name}.

 loop() ->
   io:format("Looping ~n"),
   receive
     {'EXIT', Pid, graceful} ->
       io:format("~p exiting gracefully. ~n", [Pid]),
       loop();
     {'EXIT', Pid, Reason} ->
      io:format("ERROR: ~p, ~p ~n", [Pid, Reason]),
       [[Name, Id]] = ets:select(my_sup, [{{'$1', '$2'}, [{'==', '$2', pid_to_list(Pid)}], [['$1', '$2']]}]),
       update_item(Pid, Name), loop();
     {add_item, Name} ->
       {ok, Pid} = my_mod:start(Name),
       ets:insert(?MODULE, {Name, pid_to_list(Pid)}),
       loop();
      {update_item, Pid, Name} ->
       {ok, NewPid} = my_mod:start(Name),
       ets:update_element(?MODULE, Name, {2, pid_to_list(NewPid)}),
       loop();
      {remove_item, Name} ->
        ets:delete(?MODULE, Name),
        Name ! stop, loop()
   end.

注意我现在如何从主管调用my_mod方法,并且当在my_mod中调用spawn_link时,它将链接回主管而不是shell。我还可以通过从接收循环传递命令来强制执行规定的管理程序接口add_item / 1,remove_item / 1,然后我可以执行其他不会破坏接口方法的行为的操作。我测试了以下内容:

1> c(my_sup), c(my_mod), my_sup:start_link(), my_sup:add_item(a), my_sup:add_item(b), observer:start().
Looping 
Started a
Looping 
Started b
ok
2> my_sup:remove_item(a).
Looping 
<0.43.0> exiting gracefully. 
{remove_item,a}
Looping 
3> b ! exit .
ERROR: <0.44.0>, kill 
Looping 
exit
Looping 
Started b

哦,我也花了一些时间围绕为什么我不能打电话

exit(whereis(Pid), normal).

这转变为:

&#34; ...退出呼叫(Pid,正常)。这个命令没有做任何有用的事情,因为一个进程不能被正常的原因作为参数远程杀死。&#34;

http://learnyousomeerlang.com/errors-and-processes

希望这有助于其他人......