我正在尝试为我所制作的流程编写主管。我已经调查了一段时间无济于事,所以希望有人可以提供帮助。
我对我必须使用的接口有一定的限制,因为这是用于分配的,所以我知道使用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
有人能指出我正确的方向吗?
答案 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
希望这有助于其他人......