如何在 Erlang 中使用 gen_event 调用?

Posted

技术标签:

【中文标题】如何在 Erlang 中使用 gen_event 调用?【英文标题】:How to call using gen_event in Erlang? 【发布时间】: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,#statexs=[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=#statexs=XS)->
    ok,#statexs=[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,并不代表调用成功。 【参考方案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, #statexs=[1] .

handle_event(append,Elem, #statexs=XS ) ->
    io:format("hev:handle_event() called~n"),
    ok, #statexs=[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() 函数。

【讨论】:

以上是关于如何在 Erlang 中使用 gen_event 调用?的主要内容,如果未能解决你的问题,请参考以下文章

在 Erlang 中实现图灵机

Erlang模块supervisor翻译

Erlang 源码分析之 Gen_Server

如何使用 erlang 插件在 vscode 中调试 rebar3 erlang?

如何在 Erlang shell 中运行 Erlang 应用程序

如何在 Erlang 中使用闭包?