.小麥.
一個只喜歡小白兔的大灰狼..

1. 事件处理规则

在OTP中,事件管理器是一个事件可以发送到的命名对象,一个事件可以是一个错误、一个警告、或者一些要写入日志的信息

在事件管理器中,有0个、一个或者多个事件处理器被安装,当事件管理器被一个事件通知时,这个事件将被安装在事件管理器中的事件处理器处理,

事件管理器用一个进程实现,事件处理器用回调模块实现。事件管理器本质上维护一个{Module, State}列表,每一个Module为一个事件处理器,而State为事件处理器的内部状态。

2. 例子

事件处理器的回调模块把错误信息写入终端

-module(terminal_logger).-behaviour(gen_event).-export([init/1, handle_event/2, terminate/2]).init(_Args) ->    {ok, []}.handle_event(ErrorMsg, State) ->    io:format("***Error*** ~p~n", [ErrorMsg]),    {ok, State}.terminate(_Args, _State) ->    ok.

事件处理器的回调模块把错误信息写入文件

-module(file_logger).-behaviour(gen_event).-export([init/1, handle_event/2, terminate/2]).init(File) ->    {ok, Fd} = file:open(File, read),    {ok, Fd}.handle_event(ErrorMsg, Fd) ->    io:format(Fd, "***Error*** ~p~n", [ErrorMsg]),    {ok, Fd}.terminate(_Args, Fd) ->    file:close(Fd).

 

3. 启动事件管理器

调用

gen_event:start_link({local, error_man})

启动管理器,这个函数生成并连接到一个新进程,参数{local, error_man}指定名称,在这个例子中,事件管理器被局部注册为error_man

假如忽略名称,那么事件管理器不会被注册,它的PID将被使用。名称也可以是这种形式{global, Name},这样,事件管理器的名称是用global:register_name/2注册的。

假如事件管理器是监控树的一部分,那么gen_event:start_link必须被使用,也就是被监控树启动,而gen_event:start启动单独的事件管理器,也就是事件管理器不是监控树的一部分。

 

4. 添加事件处理器

下面的例子显示怎样启动一个事件管理器和添加一个事件处理器

1> gen_event:start({local, error_man}).{ok,<0.31.0>}2> gen_event:add_handler(error_man, terminal_logger, []).ok

gen_event:add_handler(error_man, terminal_logger, [])为error_man添加处理器terminal_logger,事件管理器调用terminal_logger:init([])这个回调函数, []是参数,init要返回一个{ok, State},State是事件处理器的内部状态

init(_Args) ->    {ok, []}.

这里,init不需要任何输入参数,对于terminal_logger,也没使用内部状态,对于file_logger,内部状态保存了打开的文件描述符

init(File) ->    {ok, Fd} = file:open(File, read),    {ok, Fd}.

 

5. 关于事件通知

 

3> gen_event:notify(error_man, no_reply).***Error*** no_replyok

error_man是事件管理器的名称,no_reply是事件,事件作为消息发送给事件管理器,当事件被收到时,事件管理器为每个安装的事件处理器按安装次序调用handle_event(Event, State),这个函数期待返回{ok, State1},State1是事件处理器的新状态。

在terminal_logger中

handle_event(ErrorMsg, State) ->    io:format("***Error*** ~p~n", [ErrorMsg]),    {ok, State}.

 

在file_logger中

handle_event(ErrorMsg, Fd) ->    io:format(Fd, "***Error*** ~p~n", [ErrorMsg]),    {ok, Fd}.

 

6. 删除一个事件处理器

gen_event:delete_handler(error_man, terminal_logger, []),这个函数向事件管理器error_man发送了一个消息,告诉他删除terminal_logger这个事件处理器,事件管理器将调用terminal_logger:terminate([], State),参数[]是delete_handler的第三个参数,terminate以init相反的方向调用,以完成清理工作,返回值被忽略。

在terminal_logger中,没有清理动作

terminate(_Args, _State) ->    ok.

 

在file_logger中,文件描述符被关掉

terminate(_Args, Fd) ->    file:close(Fd).

 

7. 停止

当事件管理器被停止,它给每个注册的事件处理器调用terminate/2的机会,就好像事件处理器被删除一样。如果事件管理器是监控树的一部分,不需要显示的停止事件管理器。当事件管理器作为单独进程使用时,则调用gen_event:stop(error_man).

 

 

本文属转载

原作者:老纪
原载:
老纪博客版权所有。转载时必须以链接形式注明转载自老纪博客 [http://www.jifuyi.com/]。
本文链接地址:
http://www.jifuyi.com/one-behaviour-of-the-erlang-gen_event/

posted on 2010-03-19 10:49  酒猫  阅读(2433)  评论(0编辑  收藏  举报