1

我正在尝试让 OTP 主管启动将(最终)连接到远程服务器的童工。我使用 Rebar 创建了一个模板测试应用程序,我试图让主管在模块 'foo' 中触发功能 'hi'。它编译好并运行:

Eshell V5.8.5  (abort with ^G)
1> test_app:start(1,1).
{ok,<0.34.0>}

但是当我尝试启动工人时,它会出现这个错误:

2> test_sup:start_foo().
{error,{badarg,{foo,{foo,start_link,[]},
                    permanent,5000,worker,
                    [foo]}}}

这个问题似乎与这个问题相似,但又不一样:Erlang - 从主管模块开始一个孩子

有任何想法吗?

test_app.erl

-module(test_app).
-behaviour(application).net 
-export([start/2, stop/1]).

start(_StartType, _StartArgs) ->
    test_sup:start_link().

stop(_State) ->
    ok.

Test_sup.erl:

-module(test_sup).
-behaviour(supervisor).
-export([start_link/0]).
-export([init/1, start_foo/0]).
-define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}).

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

init([]) ->
    {ok, { {one_for_one, 5, 10}, []} }.
start_foo()->
    supervisor:check_childspecs(?CHILD(foo, worker)),
    supervisor:start_child(?MODULE, ?CHILD(foo, permanent)). 

foo.erl:

-module(foo).
-export([hi/0]).
hi()->
io:format("worker ~n").
4

2 回答 2

1

?CHILD(foo, worker)当您尝试使用宏调用以宏启动子时,您使用宏调用检查子规范?CHILD(foo, permanent)CHILD宏的第二个参数是进程类型,应该是workeror supervisor。所以第一个宏调用是正确的。该值permanent是您已经设置为的重启类型permanent的值,因此第二次调用是错误的,您会收到badarg错误消息。

注意:库函数也会产生错误是很常见的badarg,而不仅仅是内置函数。为什么它是 badarg并不总是很明显。

于 2013-10-22T23:48:29.597 回答
0

我认为罗伯特的答案是不完整的,在用工人替换永久后你仍然有一个错误返回supervisor:check_childspecs(?CHILD(foo, worker)),,我不知道为什么。

[编辑]

bard arg 的问题来自... badarg :o)

check_childspecs extepect 一个 child_specs 列表,正确的语法是supervisor:check_childspecs([?CHILD(foo, worker)]),,然后它工作正常。以下代码已更新。

[编辑结束]

但是你也会得到一个错误,因为主管会尝试启动 foo 模块中不存在的函数 foo:start_link。以下代码打印错误,但似乎工作正常。

-module(foo).
-export([hi/0,start_link/0,loop/0]).

start_link() ->
    {ok,spawn_link(?MODULE,loop,[])}.

hi()->
io:format("worker ~n").

loop() ->
    receive
        _ -> ok
    end.


-module(test_sup).
-behaviour(supervisor).
-export([start_link/0]).
-export([init/1, start_foo/0]).
-define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}).

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

init([]) ->
    {ok, { {one_for_one, 5, 10}, []} }.
start_foo()->
    io:format("~p~n",[supervisor:check_childspecs([?CHILD(foo, worker)])]),
    supervisor:start_child(?MODULE, ?CHILD(foo, worker)). 

[编辑]

回答大卫评论

在我的代码中,loop/0根本不循环,在接收块上,进程等待任何消息,一旦收到消息,进程就会终止返回值 ok。因此,只要工作进程没有收到任何消息,它就会继续存在,当您与主管进行一些测试时,这很好:o)。

相反,hi/0 函数只是在控制台上打印 'worker' 并完成。由于supervisor的重启策略是one_for_one,最大重启次数是5,子进程是永久的,supervisor会尝试启动hi进程5次,在控制台打印5次'worker',然后就放弃了并以错误消息终止自身** exception error: shutdown

通常,您应该选择permanent永无止境的进程(例如应用程序的主服务器)。对于通常在完成工作后立即死亡的进程,您应该使用temporary. 我从未使用过transient,但我读到它应该用于必须在死前完成任务的过程。

于 2013-10-23T04:16:56.067 回答