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