1. 程式人生 > >非同步、回撥、事件驅動、協程概念辨析

非同步、回撥、事件驅動、協程概念辨析

同步和非同步
面試問題什麼是非同步非阻塞
A. 同步
所謂同步,就是在發出一個功能呼叫時,在沒有得到結果之前,該呼叫就不返回。
B. 非同步
非同步的概念和同步相對。
當一個非同步過程呼叫發出後,先返回,呼叫者不會立刻得到結果。
實際處理這個呼叫的部件是在呼叫發出後,
通過狀態、通知來通知呼叫者,或通過回撥函式處理這個呼叫。
以 Socket為例,
當一個客戶端通過呼叫 Connect函式發出一個連線請求後,呼叫者執行緒不用等待結果,可立刻繼續向下執行。
當連線真正建立起來以後,socket底層會發送一個訊息通知該物件。

在這個語義環境下,阻塞與非阻塞,是指請求的受理者在處理某個請求的狀態,如果在處理這個請求的時候不能做其它事情(請求處理時間不確定),那麼稱之為阻塞,否則為非阻塞。

回撥
1、回撥是什麼:
知乎:回撥函式(callback)是什麼?
但是有些庫函式(library function)卻要求應用先傳給它一個函式,好在合適的時候呼叫,以完成目標任務。這個被傳入的、後又被呼叫的函式就稱為回撥函式(callback function)。
一個物件a將自身傳遞給另一個物件b,b在某個時間呼叫a的方法,就叫回調。

2、回撥的分類:
文末所說的阻塞式回撥就是同步回撥,非阻塞式回撥就是非同步回撥。
這裡要注意:
事件驅動機制的回撥是非同步的,回撥也可以是同步的,比如jdk實現的觀察者模式。

3、從網上擷取的一些關於回撥的說法,可以幫助理解:
我們常說的同步回撥,指的就是一個程式碼執行過程中,需要等到回撥函式完全執行完後,才能往 下走。
非同步回撥指的是一個程式碼執行到回撥函式時,他可以不需要經過回撥結束就能往下走。
而非同步回撥和同步回撥最大的不同就是非同步回撥裡新建了一個子執行緒。非同步回撥常見於請求伺服器資料,當取到資料時,會進行回撥。

回撥模型會加大我們的程式設計負擔。

事件驅動模型
事件驅動程式設計是一種程式設計正規化,這裡程式的執行流由外部事件來決定。它的特點是包含一個事件迴圈,當外部事件發生時使用回撥機制來觸發相應的處理。事件驅動是設計模式中觀察者模式的一個實現。
不用回撥函式,也可以實現事件驅動。例如:把事件訊息傳送到佇列,另外一個程序取佇列處理即可(沒有回撥函式)。
這種模型可以解耦事件傳送者和接收者之間的聯絡
事件驅動模型大體思路如下:

  1. 有一個事件(訊息)佇列;
  2. 滑鼠按下時,往這個佇列中增加一個點選事件(訊息);
  3. 有個迴圈,不斷從佇列取出事件,根據不同的事件,呼叫不同的函式,如onClick()、onKeyDown()等;
  4. 事件(訊息)一般都各自儲存各自的處理函式指標,這樣,每個訊息都有獨立的處理函式;

事件驅動實現的形式可以是兩種:1.事件發生時執行緒呼叫對應的回撥函式。2.在中斷向量上註冊事件,事件發生時執行中斷程式。
事件驅動使用的執行緒個數:可以是單執行緒如Twisted,雙執行緒如Netty。雙執行緒的事件驅動模式,可以自然地非同步回撥來實現,Netty的執行原理是個典型的雙執行緒事件驅動模型,用一個執行緒處理所有的連線,這個執行緒通常是一個迴圈的方法,當處理一個連線遇到阻塞的操作就將任務丟給其它的執行緒,主執行緒接著處理下一個連線。

事件驅動的python實現

協程
協程可以使用同步的方式寫非同步程式碼,通過庫或者語言的排程來實現併發。協程與回撥對比,優勢一目瞭然:程式碼更清晰直觀,更加符合思維習慣。
不是很瞭解,不多做介紹。

Twisted
但因為twisted屬於單執行緒非同步回撥,所以在回撥函式中的阻塞發生時reactor執行緒也會阻塞。
同步版本返回的是詩歌內容,而非同步版本返回的卻是一個deferred。
當然,這個函式是不能隨意啟用這個deferred的,因為它已經返回了。但這個函式已經啟動了一系列事件,這些事件最終將會啟用這個deferred。
一個deferred已經綁定了一系列的處理事件。當整個socket資料接收完畢後,就開始進行整個事件的處理。

在這裡插入圖片描述
那張圖顯示的是非同步程式設計時的時間片使用。在程式上是表現為三個套接字abc,a就緒,b就緒,c就緒,然後reactor run 後abc同時開始執行(這是非同步,因為abc均已返回但並未得到最後的返回結果)。a接收完所有資料之後,開始執行註冊好的Deferred,這也是非同步,因為deferred在之前已經返回。顯然,如果abc使用非同步,那麼處理過程通過回撥函式而不是輪詢來執行是一個很自然的行為。這就導致了Deferred也會是一個非同步過程。