如何在 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=<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]).
在外壳中:
~/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 插件在 vscode 中调试 rebar3 erlang?