2014-12-06 73 views
1

我正在嘗試爲我所做的過程寫一個主管。我已經調查了一段時間無濟於事,希望有人能夠提供幫助。在Erlang處理退出信號在手卷監督員?

我對接口有一定的限制,因爲這是用於賦值的,所以我知道使用列表的示例以及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 

任何人都可以點我在正確的方向?

回答

3

在您的shell中,您的add_item/1調用發生在shell進程內,而不是在supervisor進程內,這意味着supervisor未鏈接到新添加的進程,而是您的shell。在add_item/1中,您應該發送一條消息到主管進程中,告訴它啓動一個新的worker,並更改您的supervisor循環來處理該新消息並從那裏啓動worker。

+0

謝謝你的建議,促使我四處找工作,而不是最優雅的,但是這是由於實行接口... – Opentuned 2014-12-07 20:06:35

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方法spawn_link被稱爲在my_mod它將鏈接回主管而不是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

希望這會幫助別人......