基本定義
所謂事件驅動處理,簡單地說就是你點什麼按鈕(即產生什麼事件),電腦執行什麼操作(即調用什麼函式)。當然事件不僅限於用戶的操作。事件驅動的核心自然是事件。
從事件角度說,事件驅動程式的基本結構是由一個事件收集器、一個事件傳送器和一個事件處理器組成。
事件收集器專門負責收集所有事件,包括來自用戶的(如滑鼠、鍵盤事件等)、來自硬體的(如時鐘事件等)和來自軟體的(如作業系統、應用程式本身等)。
事件傳送器負責將收集器收集到的事件分發到目標對象中。
事件處理器做具體的事件回響工作,它往往要到實現階段才完全確定,因而需要運用虛函式機制(函式名往往取為類似於HandleMsg的一個名字)。對於框架的使用者來說,他們唯一能夠看到的是事件處理器。這也是他們所關心的內容。
視圖(即我們通常所說的“視窗”)是“事件驅動”應用程式的另一個要元。它是我們所說的事件傳送器的目標對象。視圖接受事件並能夠對其進行處理。當我們將事件傳送到具體的視圖時,實際上我們完成了一個根本性的變化:從傳統的流線型程式結構到事件觸發方式的轉變。這樣應用程式具備相當的柔性,可以應付種種離散的、隨機的事件。
用戶動作產生的事件
用戶動作 | 源對象 | 事件類 |
單擊按鈕 | JButton | ActionEvent |
單擊複選框 | JCheckBox | ItemEvent, ActionEvent |
單擊單選框 | JRadioButton | ItemEvent, ActionEvent |
在文本框裡按回車鍵 | JTextField | ActionEvent |
選擇一個新項目 | JComboBox | ItemEvent, ActionEvent |
視窗打開、關閉等 | Window | WindowEvent |
滑鼠按下、釋放等 | Component | MouseEvent |
鍵盤按鈕、釋放等 | Component | KeyEvent |
要理解事件驅動和程式,就需要與非事件驅動的程式進行比較。實際上,現代的程式大多是事件驅動的,比如多執行緒的程式,肯定是事件驅動的。早期則存在許多非事件驅動的程式,這樣的程式,在需要等待某個條件觸發時,會不斷地檢查這個條件,直到條件滿足,這是很浪費cpu時間的。而事件驅動的程式,則有機會釋放cpu從而進入睡眠態(注意是有機會,當然程式也可自行決定不釋放cpu),當事件觸發時被作業系統喚醒,這樣就能更加有效地使用cpu.
再說什麼是事件驅動的程式。一個典型的事件驅動的程式,就是一個死循環,並以一個執行緒的形式存在,這個死循環包括兩個部分,第一個部分是按照一定的條件接收並選擇一個要處理的事件,第二個部分就是事件的處理過程。程式的執行過程就是選擇事件和處理事件,而當沒有任何事件觸發時,程式會因查詢事件佇列失敗而進入睡眠狀態,從而釋放cpu。
事件驅動的程式,必定會直接或者間接擁有一個事件佇列,用於存儲未能及時處理的事件。
事件驅動的程式的行為,完全受外部輸入的事件控制,所以,事件驅動的系統中,存在大量這種程式,並以事件作為主要的通信方式。
事件驅動的程式,還有一個最大的好處,就是可以按照一定的順序處理佇列中的事件,而這個順序則是由事件的觸發順序決定的,這一特性往往被用於保證某些過程的原子化。
目前windows,linux,nucleus,vxworks都是事件驅動的,只有一些單片機可能是非事件驅動的。
Windows作業系統下
由於Windows本身是基於“事件驅動”模型的。因而在Windows作業系統下實現應用程式框架有相當的便利。在事件驅動程式的基本單元中,事件收集器已經由Windows系統完成;事件傳送器也已經由Windows完成了部分內容。之所以是部分而非完全是因為Windows是用C語言實現的,而不是C++。由於沒有對象,Windows將事件傳送到所謂的“視窗函式”中(儘管不是傳送到具體的對象,但應該說這是面向對象方式實現的一個變體)。要感謝Windows做了這件事。確定事件的目標所要做的工作的複雜可能要超出我們的想像。
wxWidgets的中所有可以處理事件的類都繼承自wxEvtHandler,其中包含frames,buttons,menus,even documents,所有的窗體類(即從wxWindow繼承的類)和程式類(application class).
這些類可以有一個事件表,用來綁定事件和被調用的函式(handler functions).
過程
•建立一個靜態事件表(即編譯時生成的事件表)的操作步驟
•建立一個新類(直接或間接從wxEvtHandler繼承)
•為每個要處理的事件聲明被調用的函式
•在被處理的事件所在的類的聲明中加入宏DECLARE_EVENT_TABLE
•在宏BEGIN_EVENT_TABLE... END_EVENT_TABLE(就是事件表)中將函式與枚舉的數字綁定(因為產生該類型的事件的按鈕不唯一,要用枚舉數來區分);有些事件不必與枚舉數綁定,因為產生該類型的事件的對象可以確定(比如就是this).
例
一個事件表
BEGIN_EVENT_TABLE(MyFrame,wxFrame)
EVT_MENU (wxID_ABOUT,MyFrame::OnAbout)
EVT_MENU (wxID_EⅪT,MyFrame::OnQuit)
EVT_SIZE (MyFrame::OnSize)
//不必與枚舉數綁定,因為產生該類型的事件的對象是this
EVT_BUTTON (wxID_OK,MyFrame::OnButtonOK)
END_EVENT_TABLE()
注
在事件中指定被綁定的數字,wxWidgets會將其映射到對應的函式,並調用函式
所有在事件表中被綁定的函式有相似的形式:返回值都是void,不是virtual函式,參數為wxCommandEvent類型
事件驅動處理程式
為需要處理的事件編寫相應的事件處理程式。要理解事件驅動和程式,就需要與非事件驅動的程式進行比較。實際上,現代的程式大多是事件驅動的,比如多執行緒的程式,肯定是事件驅動的。早期則存在許多非事件驅動的程式,這樣的程式,在需要等待某個條件觸發時,會不斷地檢查這個條件,直到條件滿足,這是很浪費cpu時間的。而事件驅動的程式,則有機會釋放cpu從而進入睡眠態(注意是有機會,當然程式也可自行決定不釋放cpu),當事件觸發時被作業系統喚醒,這樣就能更加有效地使用cpu。
為需要處理的事件編寫相應的事件處理程式。代碼在事件發生時執行。
delphi和java編程特點
n事件(event)表示程式某件事發生的信號。事件分為:
o外部事件:由外部用戶動作產生的事件。例如,點擊滑鼠、按鍵盤。
o內部事件:由系統內部產生的事件。例如,定時器事件。
n源對象(source object)是產生事件的對象。
事件處理驅動的一般步驟
1、確定回響事件的元素
2、為指定元素確定需要回響的事件類型
3、為指定元素的指定事件編寫相應的事件處理程式
4、將事件處理程式綁定到指定元素的指定事件
每個事件對象包含與該事件相關的屬性。getSource()方法可以獲取事件的源對象。
事件驅動處理庫
通常,我們寫伺服器處理模型的程式時,有以下幾種模型:
(1)每收到一個請求,創建一個新的進程,來處理該請求;
(2)每收到一個請求,創建一個新的執行緒,來處理該請求;
(3)每收到一個請求,放入一個事件列表,讓主進程通過非阻塞I/O方式來處理請求
上面的幾種方式,各有千秋,
第(1)種方法,由於創建新的進程的開銷比較大,所以,會導致伺服器性能比較差,但實現比較簡單。
第(2)種方式,由於要涉及到執行緒的同步,有可能會面臨死鎖等問題。
第(3)種方式,在寫應用程式代碼時,邏輯比前面兩種都複雜。
綜合考慮各方面因素,一般普遍認為第(3)種方式是大多數網路伺服器採用的方式——事件驅動處理庫。