Chinaunix首页 | 论坛 | 博客
  • 博客访问: 5096282
  • 博文数量: 921
  • 博客积分: 16037
  • 博客等级: 上将
  • 技术积分: 8469
  • 用 户 组: 普通用户
  • 注册时间: 2006-04-05 02:08
文章分类

全部博文(921)

文章存档

2020年(1)

2019年(3)

2018年(3)

2017年(6)

2016年(47)

2015年(72)

2014年(25)

2013年(72)

2012年(125)

2011年(182)

2010年(42)

2009年(14)

2008年(85)

2007年(89)

2006年(155)

分类: Python/Ruby

2012-10-22 12:00:10

1. 事件处理规则

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

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

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

2. 例子

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

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

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

  1. -module(file_logger).
  2. -behaviour(gen_event).
  3. -export([init/1, handle_event/2, terminate/2]).
  4. init(File) ->
  5.     {ok, Fd} = file:open(File, read),
  6.     {ok, Fd}.
  7. handle_event(ErrorMsg, Fd) ->
  8.     io:format(Fd, "***Error*** ~p~n", [ErrorMsg]),
  9.     {ok, Fd}.
  10. terminate(_Args, Fd) ->
  11.     file:close(Fd).
3. 启动事件管理器

调用

  1. 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. 1> gen_event:start({local, error_man}).
  2. {ok,<0.31.0>}
  3. 2> gen_event:add_handler(error_man, terminal_logger, []).
  4. ok

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


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

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


  1. init(File) ->
  2.     {ok, Fd} = file:open(File, read),
  3.     {ok, Fd}.
  5. 关于事件通知
  1. 3> gen_event:notify(error_man, no_reply).
  2. ***Error*** no_reply
  3. ok

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

在terminal_logger中

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

在file_logger中

  1. handle_event(ErrorMsg, Fd) ->
  2.     io:format(Fd, "***Error*** ~p~n", [ErrorMsg]),
  3.     {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中,没有清理动作

  1. terminate(_Args, _State) ->
  2.     ok.


  1. 在file_logger中,文件描述符被关掉
  1. terminate(_Args, Fd) ->
  2.     file:close(Fd).
7. 停止

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

转载自:




 







 

阅读(954) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~