詳解事件驅動跟消息驅動機制相比

事件驅動和異步IO
一般,咱們寫服務器處理模型的程序時,有如下幾種模型:
(1)每收到一個請求,建立一個新的進程,來處理該請求;
(2)每收到一個請求,建立一個新的線程,來處理該請求;
(3)每收到一個請求,放入一個事件列表,讓主進程經過非阻塞I/O方式來處理請求
上面的幾種方式,各有千秋,
第(1)中方法,因爲建立新的進程的開銷比較大,因此,會致使服務器性能比較差,但實現比較簡單。
第(2)種方式,因爲要涉及到線程的同步,有可能會面臨死鎖等問題。
第(3)種方式,在寫應用程序代碼時,邏輯比前面兩種都複雜。
綜合考慮各方面因素,通常廣泛認爲第(3)種方式是大多數網絡服務器採用的方式
 
看圖說話講事件驅動模型
在UI編程中,經常要對鼠標點擊進行相應,首先如何得到鼠標點擊呢?
方式一:建立一個線程,該線程一直循環檢測是否有鼠標點擊,那麼這個方式有如下幾個缺點:
1. CPU資源浪費,可能鼠標點擊的頻率很是小,可是掃描線程仍是會一直循環檢測,這會形成不少的CPU資源浪費;若是掃描鼠標點擊的接口是阻塞的呢?
2. 若是是堵塞的,又會出現下面這樣的問題,若是咱們不但要掃描鼠標點擊,還要掃描鍵盤是否按下,因爲掃描鼠標時被堵塞了,那麼可能永遠不會去掃描鍵盤;
3. 若是一個循環須要掃描的設備很是多,這又會引來響應時間的問題;
因此,該方式是很是很差的。html

方式二:就是事件驅動模型
目前大部分的UI編程都是事件驅動模型,如不少UI平臺都會提供onClick()事件,這個事件就表明鼠標按下事件。事件驅動模型大致思路以下:
1. 有一個事件(消息)隊列;
2. 鼠標按下時,往這個隊列中增長一個點擊事件(消息);
3. 有個循環,不斷從隊列取出事件,根據不一樣的事件,調用不一樣的函數,如onClick()、onKeyDown()等;
4. 事件(消息)通常都各自保存各自的處理函數指針,這樣,每一個消息都有獨立的處理函數;linux

 

 

 

事件驅動編程是一種編程範式,這裏程序的執行流由外部事件來決定。它的特色是包含一個事件循環,當外部事件發生時使用回調機制來觸發相應的處理。另外兩種常見的編程範式是(單線程)同步以及多線程編程。程序員

讓咱們用例子來比較和對比一下單線程、多線程以及事件驅動編程模型。下圖展現了隨着時間的推移,這三種模式下程序所作的工做。這個程序有3個任務須要完成,每一個任務都在等待I/O操做時阻塞自身。阻塞在I/O操做上所花費的時間已經用灰色框標示出來了。編程

 

 

在單線程同步模型中,任務按照順序執行。若是某個任務由於I/O而阻塞,其餘全部的任務都必須等待,直到它完成以後它們才能依次執行。這種明確的執行順序和串行化處理的行爲是很容易推斷得出的。若是任務之間並無互相依賴的關係,但仍然須要互相等待的話這就使得程序沒必要要的下降了運行速度。windows

在多線程版本中,這3個任務分別在獨立的線程中執行。這些線程由操做系統來管理,在多處理器系統上能夠並行處理,或者在單處理器系統上交錯執行。這使得當某個線程阻塞在某個資源的同時其餘線程得以繼續執行。與完成相似功能的同步程序相比,這種方式更有效率,但程序員必須寫代碼來保護共享資源,防止其被多個線程同時訪問。多線程程序更加難以推斷,由於這類程序不得不經過線程同步機制如鎖、可重入函數、線程局部存儲或者其餘機制來處理線程安全問題,若是實現不當就會致使出現微妙且使人痛不欲生的bug。安全

在事件驅動版本的程序中,3個任務交錯執行,但仍然在一個單獨的線程控制中。當處理I/O或者其餘昂貴的操做時,註冊一個回調到事件循環中,而後當I/O操做完成時繼續執行。回調描述了該如何處理某個事件。事件循環輪詢全部的事件,當事件到來時將它們分配給等待處理事件的回調函數。這種方式讓程序儘量的得以執行而不須要用到額外的線程。事件驅動型程序比多線程程序更容易推斷出行爲,由於程序員不須要關心線程安全問題。服務器

當咱們面對以下的環境時,事件驅動模型一般是一個好的選擇:網絡

程序中有許多任務,並且…
任務之間高度獨立(所以它們不須要互相通訊,或者等待彼此)並且…
在等待事件到來時,某些任務會阻塞。
當應用程序須要在任務間共享可變的數據時,這也是一個不錯的選擇,由於這裏不須要採用同步處理。多線程

網絡應用程序一般都有上述這些特色,這使得它們可以很好的契合事件驅動編程模型。
框架

事件驅動機制跟消息驅動機制相比

 
事件:按下鼠標,按下鍵盤,按下游戲手柄,將U盤插入USB接口,都將產生事件。好比說按下鼠標左鍵,將產生鼠標左鍵被按下的事件。
 消息:當鼠標被按下,產生了鼠標按下事件,windows偵測到這一事件的發生,隨即發出鼠標被按下的消息到消息隊列中,這消息附帶了一系列相關的事件信息,好比鼠標哪一個鍵被按了,在哪一個窗口被按的,按下點的座標是多少?如此等等。
 
1.要理解事件驅動和程序,就須要與非事件驅動的程序進行比較。實際上,現代的程序大可能是事件驅動的,好比多線程的程序,確定是事件驅動的。早期則存在許多非事件驅動的程序,這樣的程序,在須要等待某個條件觸發時,會不斷地檢查這個條件,直到條件知足,這是很浪費cpu時間的。而事件驅動的程序,則有機會釋放cpu從而進入睡眠態(注意是有機會,固然程序也可自行決定不釋放cpu),當事件觸發時被操做系統喚醒,這樣就能更加有效地使用cpu.
2.再說什麼是事件驅動的程序。一個典型的事件驅動的程序,就是一個死循環,並以一個線程的形式存在,這個死循環包括兩個部分,第一個部分是按照必定的條件接收並選擇一個要處理的事件,第二個部分就是事件的處理過程。程序的執行過程就是選擇事件和處理事件,而當沒有任何事件觸發時,程序會因查詢事件隊列失敗而進入睡眠狀態,從而釋放cpu。
3.事件驅動的程序,一定會直接或者間接擁有一個事件隊列,用於存儲未能及時處理的事件。
4.事件驅動的程序的行爲,徹底受外部輸入的事件控制,因此,事件驅動的系統中,存在大量這種程序,並以事件做爲主要的通訊方式。
5.事件驅動的程序,還有一個最大的好處,就是能夠按照必定的順序處理隊列中的事件,而這個順序則是由事件的觸發順序決定的,這一特性每每被用於保證某些過程的原子化。
6.目前windows,linux,nucleus,vxworks都是事件驅動的,只有一些單片機多是非事件驅動的。
事件模式耦合高,同模塊內好用;消息模式耦合低,跨模塊好用。事件模式集成其它語言比較繁瑣,消息模式集成其餘語言比較輕鬆。事件是侵入式設計,霸佔你的主循環;消息是非侵入式設計,將主循環該怎樣設計的自由留給用戶。若是你在設計一個東西猶豫不定,那麼你能夠參考win32的GetMessage,自己就是一個藕合度極低的接口,又足夠自由,接口任何語言都很方便,具體應用場景再在其基礎上封裝成事件並非難事,接口耦合較低,即使哪天事件框架調整,修改外層便可,不會傷經動骨。而若是直接實現成事件,那就徹底反過來了。
相關文章
相關標籤/搜索