【问题标题】:How to call using gen_event in Erlang?如何在 Erlang 中使用 gen_event 调用?
【发布时间】:2020-04-14 05:19:54
【问题描述】:

我正在使用gen_event 行为,当我尝试发出gen_event:call 时,我收到以下错误:

> =CRASH REPORT==== 22-Dec-2019::19:17:43.030000 ===   crasher:
>     initial call: gen_event:init_it/6
>     pid: <0.215.0>
>     registered_name: hev
>     exception exit: {undef,[{fm,state,[<0.215.0>],[]},
>                             {erl_eval,do_apply,6,
>                                       [{file,"erl_eval.erl"},{line,684}]},
>                             {shell,exprs,7,[{file,"shell.erl"},{line,686}]},
>                             {shell,eval_exprs,7,
>                                    [{file,"shell.erl"},{line,642}]},
>                             {shell,eval_loop,3,
>                                    [{file,"shell.erl"},{line,627}]}]}
>       in function  gen_event:terminate_server/4 (gen_event.erl, line 354)
>     ancestors: [<0.212.0>]
>     message_queue_len: 1
>     messages: [{'EXIT',<0.212.0>,normal}]
>     links: []
>     dictionary: []
>     trap_exit: true
>     status: running
>     heap_size: 610
>     stack_size: 27
>     reductions: 279   neighbours:

我的事件管理器和事件处理程序生成,我可以成功发出notify(我得到ok)但我不能call

模块

-module(hev).
-export([start/0,append/2,state/1]).
-export([init/1,terminate/2,code_change/3,handle_call/2,handle_event/2]).
-record(state,{
    xs=[]
}).
-behaviour(gen_event).
%callbacks
init([])->
    {ok,#state{xs=[1]}}.

**API**

start()->
    {ok,Pid}=gen_event:start_link({local,?MODULE}),
    gen_event:add_handler(Pid,some_handler,[]),
    Pid.
append(Pid,Elem)->
    gen_event:notify(Pid,{append,Elem}).
state(Pid)->
    gen_event:call(Pid,state).

处理程序

handle_event({append,Elem},State=#state{xs=XS})->
    {ok,#state{xs=[Elem|XS]}};

handle_call(state,State})->     
    {ok,State,State};
handle_call(Event,State)->
    {ok,nada_for_you,State}.

P.S我没有发布所有需要的方法(code_change,terminate..etc),但它们存在。

【问题讨论】:

  • 乍一看,问题出在gen_event:call(Pid,state)。没有导出gen_event:call/2 函数,只有gen_event:call/{3,4},这意味着您必须将处理程序显式传递给gen_event:call。另外请注意gen_event:notify/2无论如何都会返回:ok,并不代表调用成功。

标签: events erlang erlang-otp fsm


【解决方案1】:

我没有发布所有需要的方法(code_change、terminate..etc),但它们存在。

1) 无论如何,它们都是可选的。检查文档中的大绿色注释,例如terminate().

2) 至于你的错误信息:

pid: <0.215.0>
registered_name: hev
exception exit: {undef,[{fm,state,[<0.215.0>],[]},

似乎是在说有一个进程(pid=&lt;0.215.0&gt;),它以名称hev 注册,它试图执行一个名为fm:state() 的函数,但没有函数fm:state/1 定义在任何地方,因此出现 undef 异常。由于您未发布名为 fm 的模块,因此该错误与您发布的代码无关。

3) 您的代码还指定了一个名为 some_handler 的模块,但该模块不存在:

gen_event:add_handler(Pid,some_handler,[]),

4) 这里有一个基本的语法错误:

 handle_call(state,State})-> 

5) 您调用了几个带有错误数量参数的函数。

您需要更加勤奋地发布实际上会产生您遇到的错误的代码。

这是一个使用gen_event 创建计数器的简单示例:

-module(counter).
-behaviour(gen_event).
-compile(export_all).

%% Callback functions:

init(StartingCount) ->  % Called by gen_event:add_handler()
    State = StartingCount,
    {ok, State}.

terminate(_Reason, State) ->
    io:format("Terminating state was: ~w~n", [State]).

% Calls to gen_event:notify() cause this function to execute:
handle_event({increase, Change}, State) -> 
    NewState = State+Change,
    {ok, NewState};
handle_event({decrease, Change}, State) ->
    NewState = State-Change,
    {ok, NewState}.

% Calls to gen_event:call() cause this function to execute:
handle_call(get_count, State) ->  
    Reply = io_lib:format("Reply from handle_call(): count is ~w~n", [State]),
    {ok, Reply, State};
handle_call({increase_and_get_count, Change}, State) ->
    NewState = State+Change,
    Reply = io_lib:format("Reply from handle_call(): count is ~w~n", [NewState]),
    {ok, Reply, NewState}.


%% User interface functions:

start(StartingCount) ->
    ServerName = gen_event_counter,
    CallbackModule = counter,

    {ok, _Pid} = gen_event:start_link({local, ServerName}),  
    %Name of process running gen_event server is: gen_event_counter

    ok = gen_event:add_handler(ServerName, CallbackModule, StartingCount). 
    %StartingCount is passed to init() callback


stop() ->
    ok = gen_event:stop(gen_event_counter),
    stopped.

send_request_with_notify(Request) -> 
    gen_event:notify(gen_event_counter, Request). % returns immediately, does not wait for a reply.
    % Request = {increase, 1}, {decrease, 2}, etc. 
    % Request is passed as first arg to handle_event().


send_request_with_call(Request) -> 
    Reply = gen_event:call(gen_event_counter, counter, Request), % waits for a reply  
    % Request is passed as first arg to handle_call()
    io:format("send_request_with_call() returned => ~s", [Reply]).

在外壳中:

~/erlang_programs/gen_event$ erl
Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V9.3  (abort with ^G)

1> c(counter).                                                 
counter.erl:3: Warning: export_all flag enabled - all functions will be exported
{ok,counter}

2> counter:start(0).                                           
ok

3> counter:send_request_with_call(get_count).                  
send_request_with_call() returned => Reply from handle_call(): count is 0
ok

4> counter:send_request_with_notify({increase, 2}).            
ok

5> counter:send_request_with_call(get_count).      
send_request_with_call() returned => Reply from handle_call(): count is 2
ok

6> counter:send_request_with_call({increase_and_get_count, 5}).
send_request_with_call() returned => Reply from handle_call(): count is 7
ok

7> counter:stop().                                             
Terminating state was: 7
stopped

8> 

修复代码中的所有错误后:

-module(hev).
-compile(export_all).
-behaviour(gen_event).

-record(state,{
    xs=[]
}).

%callbacks
init(no_args)->
    {ok, #state{xs=[1]} }.

handle_event({append,Elem}, #state{xs=XS} ) ->
    io:format("hev:handle_event() called~n"),
    {ok, #state{xs=[Elem|XS]}}.

handle_call(get_state, State)->     
    Reply = State,
    {ok, Reply, State};
handle_call(_Other, State)->
    Reply = nada_for_you,
    {ok, Reply, State}.

%**API**

start()->
    gen_event:start_link({local, ?MODULE}),  %Sets the gen_event server name to ?MODULE

    %                     Server   Callback  Args for
    %                     Name     module    init()
    gen_event:add_handler(?MODULE, ?MODULE, no_args). 
    %Tells the gen_event server named ?MODULE to look for the callback functions 
    %in a module also named ?MODULE

append(Elem)->
    %                Server    Request
    %                Name      (matches against 1st arg in handle_event() )
    gen_event:notify(?MODULE, {append, Elem}).

get_state()->
    %              Server   Calback  Request
    %              Name     module   (matches against 1st arg in handle_call() )
    gen_event:call(?MODULE, ?MODULE, get_state).

other_calls() ->
    gen_event:call(?MODULE, ?MODULE, {set_state, [1, 2, 3]}).


stop() ->
    ok = gen_event:stop(?MODULE),
    stopped.

在外壳中:

~/erlang_programs/gen_event$ erl
Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V9.3  (abort with ^G)

1> c(hev).         
hev.erl:2: Warning: export_all flag enabled - all functions will be exported
{ok,hev}

2> hev:start().    
ok

3> hev:get_state().
{state,[1]}

4> hev:append(45). 
ok
hev:handle_event() called

5> hev:get_state().
{state,[45,1]}

6> hev:other_calls().
nada_for_you

7> hev:stop().
stopped

8> 

请注意,notify() 会导致所有添加了add_handler() 的模块中的handle_event() 函数执行,而call() 针对特定模块的handle_call() 函数。

【讨论】:

    猜你喜欢
    • 2011-06-16
    • 1970-01-01
    • 2010-12-21
    • 1970-01-01
    • 1970-01-01
    • 2021-12-03
    • 2016-05-18
    • 2015-11-09
    • 2013-06-03
    相关资源
    最近更新 更多