一、什么是gen_event
按照书上定义在OTP中,它由通用事件管理器进程组成,该进程具有动态添加和删除的任意数量的事件处理程序。事件可以是例如错误,警报或要记录的某些信息
简单来说,就是gen_event行为运行一个了一个事件管理进程,该进程接受消息(事件),并根据消息(事件)做对应的事件处理,而提供的对应事件处理其实就是添加的“回调函数”(事件处理器)。与gen_server不一样的是,他把回调的函数分类成多个module并称为事件处理器,需要注意的是事件处理器不是一个进程,看源码你会发现只是事件管理器进程里构造了一个记录列表,里面储存了对应的module和函数参数,必要时就调用。gen_event在游戏开发中除了在日记模块有用到,其他的地方几乎没有应用
二、如何使用
使用OTP事件管理器需要做四件事:
- 编写事件处理程序,
- 创建事件管理器,
- 向管理器添加处理程序,
- 通知事件管理器
以erlang趣学指南的冰球比赛为案例
事件管理器
-module(curling).
-export([start_link/2, set_teams/3, add_points/3, next_round/1]).
-export([join_feed/2, leave_feed/2]).
-export([game_info/1]).
start_link(TeamA, TeamB) ->
%% 创建事件管理进程
{ok, Pid} = gen_event:start_link(),
%% 添加事件处理器 gen_event:add_handler(事件管理进程pid, 回调模块, 参数),curling_scoreboard为计分板
gen_event:add_handler(Pid, curling_scoreboard, []),
%% accumulator为累加器
gen_event:add_handler(Pid, curling_accumulator, []),
set_teams(Pid, TeamA, TeamB),
{ok, Pid}.
%%设置队伍
set_teams(Pid, TeamA, TeamB) ->
%% 事件通知
gen_event:notify(Pid, {set_teams, TeamA, TeamB}).
%%添加分数
add_points(Pid, Team, N) ->
gen_event:notify(Pid, {add_points, Team, N}).
%%下一回合
next_round(Pid) ->
gen_event:notify(Pid, next_round).
%% Subscribes the pid ToPid to the event feed.
%% The specific event handler for the newsfeed is
%% returned in case someone wants to leave
%%join_feed(Pid, ToPid) ->
%% HandlerId = {curling_feed, make_ref()},
%% gen_event:add_sup_handler(Pid, HandlerId, [ToPid]),
%% HandlerId.
%%
%%leave_feed(Pid, HandlerId) ->
%% gen_event:delete_handler(Pid, HandlerId, leave_feed).
%% Returns the current game state.
game_info(Pid) ->
gen_event:call(Pid, curling_accumulator, game_data).
事件处理器
-module(curling_scoreboard).
-behaviour(gen_event).
-export([init/1, handle_event/2, handle_call/2, handle_info/2, code_change/3,
terminate/2]).
%%计分板
init([]) ->
io:format("curling_scoreboard init ~n"),
{ok, []}.
handle_event({set_teams, TeamA, TeamB}, State) ->
io:format("curling_scoreboard set_teams ~n"),
io:format("Scoreboard: Team ~s vs. Team ~s~n", [TeamA, TeamB]),
%% curling_scoreboard_hw:set_teams(TeamA, TeamB),
{ok, State};
handle_event({add_points, Team, N}, State) ->
io:format("curling_scoreboard add_points ~n"),
[io:format("Scoreboard: increased score of team ~s by 1~n", [Team]) || _ <- lists:seq(1,N)],
%% [curling_scoreboard_hw:add_point(Team) || _ <- lists:seq(1,N)],
{ok, State};
handle_event(next_round, State) ->
io:format("curling_scoreboard next_round ~n"),
io:format("Scoreboard: round over~n"),
%% curling_scoreboard_hw:next_round(),
{ok, State};
handle_event(_, State) ->
{ok, State}.
handle_call(_, State) ->
{ok, ok, State}.
handle_info(_, State) ->
{ok, State}.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
terminate(_Reason, _State) ->
ok.
-module(curling_accumulator).
-behaviour(gen_event).
-export([init/1, handle_event/2, handle_call/2, handle_info/2, code_change/3,
terminate/2]).
-record(state, {teams=orddict:new(), round=0}).
%% 累加器
init([]) ->
io:format("accumulator init ~n"),
{ok, #state{}}.
handle_event({set_teams, TeamA, TeamB}, S=#state{teams=T}) ->
io:format("accumulator set_teams ~n"),
Teams = orddict:store(TeamA, 0, orddict:store(TeamB, 0, T)),
{ok, S#state{teams=Teams}};
handle_event({add_points, Team, N}, S=#state{teams=T}) ->
io:format("accumulator add_points ~n"),
Teams = orddict:update_counter(Team, N, T),
{ok, S#state{teams=Teams}};
handle_event(next_round, S=#state{}) ->
io:format("accu