【问题标题】:Handling exit signals in hand rolled Supervisors in Erlang?在 Erlang 的手卷主管中处理退出信号?
【发布时间】:2015-02-04 17:32:39
【问题描述】:

我正在尝试为我创建的流程编写主管。我已经对此进行了一段时间的调查,但无济于事,希望有人能提供帮助。

我对必须使用的接口有一定的限制,因为这是用于分配的,所以我知道使用列表的示例以及 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

谁能指出我正确的方向?

【问题讨论】:

    标签: erlang exit erlang-supervisor


    【解决方案1】:

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

    【讨论】:

    • 感谢您的建议,这促使我找到了一个解决方法,不是最优雅的,但这是由于强加的界面......
    【解决方案2】:

    好的,正如 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).
    

    原来是这样解释的:

    "...对 exit(Pid, normal) 的调用。此命令没有任何用处,因为无法以正常原因作为参数远程杀死进程。"

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

    希望这对其他人有所帮助...

    【讨论】:

      猜你喜欢
      • 2011-01-28
      • 2015-05-24
      • 2013-01-17
      • 1970-01-01
      • 2015-03-16
      • 2014-10-05
      • 1970-01-01
      • 2013-05-14
      • 2012-06-22
      相关资源
      最近更新 更多