1

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

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

谁能指出我正确的方向?

4

2 回答 2

3

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

于 2014-12-06T19:46:52.290 回答
1

好的,正如史蒂夫 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

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

于 2014-12-07T20:22:22.463 回答