erlang四大behaviour之三-gen_event(轉載)



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/]。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章