如何在 Erlang 中使用 gen_event 调用?

How to call using gen_event in Erlang?

我正在使用 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、终止等),但它们存在。

I have not posted all the requried methods (code_change,terminate..etc) but they exist.

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

2) 至于你的错误信息:

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

似乎是说有一个进程(pid=<0.215.0>)注册了名称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]).

在shell中:

~/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.

在shell中:

~/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() 函数。