【问题标题】:Erlang noproc error when trying to terminate child processErlang noproc 尝试终止子进程时出错
【发布时间】:2017-05-24 16:21:16
【问题描述】:

我尝试设计一台服务器在收到请求时启动临时进程。但是,当我尝试终止或终止临时进程时,出现noproc 错误。谁能告诉我如何解决这个问题?

我得到了以下文件:

temp.erl(主要主管)

-module(temp).
-behaviour(supervisor).
-export([start_link/0, init/1]).
-export([list_children/0]).

start_link() ->
    supervisor:start_link({local, ?MODULE}, ?MODULE, []).

list_children() ->
    supervisor:which_children(?MODULE).

init([]) ->
    process_flag(trap_exit, true),
    io:format("****Main supervisor****~n"),
    {ok, {{one_for_one, 5, 10}, 
          [{temp_srv, {temp_srv, start_link, []}, 
            permanent, infinity, worker, [temp_srv]}, 
           {temp_sup, {temp_sup, start_link, []}, 
            permanent, infinity, supervisor, [temp_sup]}]}}.

temp_srv.erl(服务器)

-module(temp_srv).
-behaviour(gen_server).
-export([init/1, handle_cast/2, handle_info/2, handle_call/3, terminate/2, code_change/3]).
-export([start_link/0, echo_call/1]).

-record(state, {}).

-define(SERVER, ?MODULE).

echo_call(Data) ->
    gen_server:call(?SERVER, {echo, Data}).

start_link() ->
    gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).

init([]) ->
    process_flag(trap_exit, true),
    {ok, #state{}}.

handle_call({echo, Data}, _From, State) ->
    Result = temp_sup:assign_task({echo, Data}),
    {reply, Result, State};
handle_call(_Request, _From, State) ->
    {noreply, State}.

handle_info(_Info, State) ->
    {noreply, State}.

handle_cast(_Request, State) ->
    {noreply, State}.

terminate(_Reason, _State) ->
    ok.

code_change(_Old, State, _Extra) ->
    {ok, State}.

temp_sup.erl(启动临时进程的主管)

-module(temp_sup).
-behaviour(supervisor).
-export([start_link/0, init/1]).
-export([start_temp_worker/0, list_children/0, assign_task/1, select_available_children/1, select_finished_children/1]).

start_link() ->
    supervisor:start_link({local, ?MODULE}, ?MODULE, []).

list_children() ->
        supervisor:which_children(?MODULE).

init([]) ->
    io:format("****Temp supervisor*****~n"),
    spawn_link(fun start_pool/0),
    {ok, {{simple_one_for_one, 0, 1}, [{temp_worker, {temp_worker, start_link, []}, temporary, 1000, worker, [temp_worker]}]}}.

start_pool() ->
        [start_temp_worker() || _ <- lists:seq(1, 3)].

start_temp_worker() ->
        supervisor:start_child(?MODULE, []).

assign_task(Data) ->
        case list_children() of
                [] ->
                        {ok, Pid} = start_temp_worker(),
                        gen_server:call(Pid, Data);
                ChildL when is_list(ChildL) ->
            case select_finished_children(ChildL) of
                no_pid_to_kill -> ok;
                {ok, KPid} -> gen_server:call(KPid, stop)
            end,
            {ok, Pid} = select_available_children(ChildL),
            start_temp_worker(),
            io:format("Assign task to ~p~n", [Pid]),
            gen_server:call(Pid, Data)
        end.

select_available_children([ChildH | ChildT]) ->
    {undefined, Pid, worker, [temp_worker]} = ChildH,
    case gen_server:call(Pid, check_status) of
        active -> 
            io:format("Find available children ~p~n", [Pid]),
            {ok, Pid};
        running -> select_available_children(ChildT);
        done -> select_available_children(ChildT)
    end.

select_finished_children([ChildH | ChildT]) ->
    {undefined, KPid, worker, [temp_worker]} = ChildH,
    case gen_server:call(KPid, check_status) of
        done -> 
            io:format("Find process ~p to kill~n", [KPid]),
            {ok, KPid};
        _ -> select_finished_children(ChildT)
    end;
select_finished_children([]) -> no_pid_to_kill.

temp_worker.erl(临时进程)

-module(temp_worker).
-behaviour(gen_server).
-export([start_link/0, init/1]).
-export([handle_info/2, handle_call/3, handle_cast/2, code_change/3, terminate/2]).
-record(state, {status}).

start_link() ->
    gen_server:start_link(?MODULE, [], []).

init([]) ->
    process_flag(trap_exit, true),
    io:format("Start temporary worker process ~p~n", [self()]),
    {ok, #state{status = active}}.

handle_info(_Request, State) ->
    {noreply, State}.

handle_call(check_status, _From, #state{status = Status} = State) ->
    {reply, Status, State};
handle_call({echo, Msg}, _From, State) ->
    io:format("~p get echo request for message ~p~n", [self(), Msg]),
    {reply, Msg, State#state{status = done}};
handle_call(stop, _From, State) ->
    {stop, normal, stopped, State};
handle_call(_Request, _From, State) ->
    {noreply, State}.

handle_cast(_Request, State) ->
    {noreply, State}.

code_change(_Old, State, _Extra) ->
    {ok, State}.

terminate(_Reason, _State) ->
    ok.

这个设计应该是在临时主管启动后启动三个临时进程,一旦收到请求,就会终止一个已经完成工作的临时进程。

这就是我运行这些代码的方式:

9> temp:start_link().      
****Main supervisor****
****Temp supervisor*****
Start temporary worker process <0.98.0>
{ok,<0.94.0>}
Start temporary worker process <0.99.0>
Start temporary worker process <0.101.0>
10> temp_srv:echo_call(asd).
Find available children <0.98.0>
Start temporary worker process <0.102.0>
Assign task to <0.98.0>
<0.98.0> get echo request for message asd
asd
11> temp_srv:echo_call(asd).
Find process <0.98.0> to kill

=ERROR REPORT==== 23-May-2017::23:16:34 ===
** Generic server temp_srv terminating 
** Last message in was {echo,asd}
** When Server state == {state}
** Reason for termination == 
** {{noproc,{gen_server,call,[<0.98.0>,check_status]}},
    [{gen_server,call,2,[{file,"gen_server.erl"},{line,204}]},
     {temp_sup,select_available_children,1,[{file,"temp_sup.erl"},{line,41}]},
     {temp_sup,assign_task,1,[{file,"temp_sup.erl"},{line,33}]},
     {temp_srv,handle_call,3,[{file,"temp_srv.erl"},{line,21}]},
     {gen_server,try_handle_call,4,[{file,"gen_server.erl"},{line,615}]},
     {gen_server,handle_msg,5,[{file,"gen_server.erl"},{line,647}]},
     {proc_lib,init_p_do_apply,3,[{file,"proc_lib.erl"},{line,247}]}]}

=ERROR REPORT==== 23-May-2017::23:16:34 ===
** Generic server temp terminating 
** Last message in was {'EXIT',<0.92.0>,
                           {{{noproc,
                                 {gen_server,call,[<0.98.0>,check_status]}},
                             {gen_server,call,[temp_srv,{echo,asd}]}},
                            [{gen_server,call,2,
                                 [{file,"gen_server.erl"},{line,204}]},
                             {erl_eval,do_apply,6,
                                 [{file,"erl_eval.erl"},{line,674}]},
                             {shell,exprs,7,[{file,"shell.erl"},{line,686}]},
                             {shell,eval_exprs,7,
                                 [{file,"shell.erl"},{line,641}]},
                             {shell,eval_loop,3,
                                 [{file,"shell.erl"},{line,626}]}]}}
** When Server state == {state,
                            {local,temp},
                            one_for_one,
                            [{child,<0.96.0>,temp_sup,
                                 {temp_sup,start_link,[]},
                                 permanent,infinity,supervisor,
                                 [temp_sup]},
                             {child,<0.104.0>,temp_srv,
                                 {temp_srv,start_link,[]},
                                 permanent,infinity,worker,
                                 [temp_srv]}],
                            undefined,5,10,
                            [-576459529],
                            0,temp,[]}
** Reason for termination == 
** {{{noproc,{gen_server,call,[<0.98.0>,check_status]}},
     {gen_server,call,[temp_srv,{echo,asd}]}},
    [{gen_server,call,2,[{file,"gen_server.erl"},{line,204}]},
     {erl_eval,do_apply,6,[{file,"erl_eval.erl"},{line,674}]},
     {shell,exprs,7,[{file,"shell.erl"},{line,686}]},
     {shell,eval_exprs,7,[{file,"shell.erl"},{line,641}]},
     {shell,eval_loop,3,[{file,"shell.erl"},{line,626}]}]}
** exception exit: {{noproc,{gen_server,call,[<0.98.0>,check_status]}},
                    {gen_server,call,[temp_srv,{echo,asd}]}}
     in function  gen_server:call/2 (gen_server.erl, line 204)

【问题讨论】:

    标签: erlang


    【解决方案1】:

    来自error docs

    noproc -- 试图链接到一个不存在的进程。

    错误消息说有问题的行是:

    case gen_server:call(Pid, check_status) of
    

    这意味着 Pid 是一个不存在的进程。在您的输出中,您可以看到:

    Assign task to <0.98.0>
    <0.98.0> get echo request for message asd
    asd
    

    我认为这意味着进程 已完成执行并已终止。您可以通过输出process_info(Pid) 来验证这一点,这将为不存在的进程返回undefined。该过程位于您的ChildL 列表的首位。您可以使用io:format() 语句验证这一点:

        assign_task(Data) ->
            ...
            case select_finished_children(ChildL) of
                no_pid_to_kill -> ok;
                {ok, KPid} -> gen_server:call(KPid, stop)
            end,
            io:format("assign_task(): ChildL: ~w~n", [ChildL]),  %%<***HERE
            io:format("****Info: ~w~n", [process_info(Pid)]),    %%<***HERE
            {ok, Pid} = select_available_children(ChildL),
    

    然后你的代码会这样做:

    select_available_children([ChildH | ChildT]) ->
        {undefined, Pid, worker, [temp_worker]} = ChildH,
        case gen_server:call(Pid, check_status) of
    

    ChildH 包含 pid &lt;0.98.0&gt;,它已终止,并且该 pid 绑定到 Pid。然后gen_server:call(Pid) 执行,导致noproc 错误。

    【讨论】:

    • 我用process_info查看了进程,结果显示进程还活着,它的状态是waiting。
    • @billcyz,来吧。在它说状态是等待之后,没有错误。
    • 你什么时候检查过这个过程?如果您在 erlang 崩溃后进行检查,那么您将找不到任何东西,即使它的主管消失了。
    • @billcyz,添加我显示的行,完全按照您在问题中所做的那样运行代码,在输出中找到错误消息,向上滚动到错误的第一行,然后告诉我什么错误消息第一行之前的第一行说?
    • 问题解决了。我认为子列表会导致问题。我将代码更改为:assign_task(Data) -> case select_finished_children(list_children()) of no_pid_to_kill -> ok; {ok, KPid} -> stopped = gen_server:call(KPid, stop) end, 问题解决了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-07-31
    • 2011-06-13
    • 2015-11-05
    • 1970-01-01
    • 2011-01-21
    • 2014-06-19
    • 2020-08-30
    相关资源
    最近更新 更多