對於同步、非同步、阻塞、非阻塞的幾點淺薄理解

嘟~嘟發表於2018-08-29

首先我們先來看看:

一、同步與非同步
同步/非同步, 它們是訊息的通知機制(都是通過狀態、通知、回撥函式來返回結果)

1. 概念解釋
    同步
所謂同步,就是在發出一個功能呼叫時,在沒有得到結果之前,該呼叫就不返回。

(按照這個定義,其實絕大多數函式都是同步呼叫(例如sin isdigit等)。
但是一般而言,我們在說同步、非同步的時候,特指那些需要其他部件協作或者需要一定時間完成的任務。
最常見的例子就是 SendMessage。
該函式傳送一個訊息給某個視窗,在對方處理完訊息之前,這個函式不返回。
當對方處理完畢以後,該函式才把訊息處理函式所返回的值返回給呼叫者。)

 

    非同步

恰恰與同步相反。非同步的概念和同步相對。
當一個非同步過程呼叫發出後,呼叫者不會立刻得到結果。
實際處理這個呼叫的部件是在呼叫發出後,
通過狀態、通知來通知呼叫者,或通過回撥函式處理這個呼叫

(以 Socket為例,
當一個客戶端通過呼叫 Connect函式發出一個連線請求後,呼叫者執行緒不用等待結果,可立刻繼續向下執行。
當連線真正建立起來以後,socket底層會傳送一個訊息通知該物件。)

 

2.擴充兩者在實際程式中的異同:

 在實際的程式中,
同步訊息通知機制:就好比簡單的read/write 操作,它們需要等待這兩個操作成功才能返回;
                  同步, 是由處理訊息者自己去等待訊息是否被觸發;
非同步訊息通知機制:類似於select/poll 之類的多路複用IO 操作,
                  當所關注的訊息被觸發時,由訊息觸發機制通知觸發對訊息的處理.
                  非同步, 由觸發機制來通知處理訊息者;

還是回到上面的例子,
輪到你辦理業務, 這個就是你關注的訊息,
而辦理什麼業務, 就是對這個訊息的處理,
兩者是有區別的.

而在真實的IO 操作時: 所關注的訊息就是     該fd是否可讀寫,
                     而對訊息的處理是     對這個fd 進行讀寫.

同步/非同步僅僅關注的是如何通知訊息,它們對如何處理訊息並不關心,
好比說,銀行的人僅僅通知你輪到你辦理業務了,
而辦理業務什麼業務(存錢還是取錢)他們是不知道的.

二、阻塞與非阻塞
阻塞/非阻塞, 它們是程式在等待訊息(無所謂同步或者非同步)時的狀態.

1. 概念解釋
    阻塞
阻塞呼叫是指呼叫結果返回之前,當前執行緒會被掛起。函式只有在得到結果之後才會返回。

    非阻塞
非阻塞和阻塞的概念相對應,指在不能立刻得到結果之前,該函式不會阻塞當前執行緒,而會立刻返回。

 

2. 舉例說明
繼續上面的那個例子,
不論是排隊等待,還是使用號碼等待通知,
如果在這個等待的過程中,
. 等待者除了等待訊息之外不能做其它的事情,那麼該機制就是阻塞的,
  表現在程式中,也就是該程式一直阻塞在該函式呼叫處不能繼續往下執行.
. 相反,有的人喜歡在銀行辦理這些業務的時候一邊打打電話發發簡訊一邊等待,這樣的狀態就是非阻塞的,
  因為他(等待者)沒有阻塞在這個訊息通知上,而是一邊做自己的事情一邊等待.

 

 

三、易混淆的點
很多人也會把非同步和非阻塞混淆,
因為非同步操作一般都不會在真正的IO 操作處被阻塞,
比如如果用select 函式,當select 返回可讀時再去read 一般都不會被阻塞
就好比當你的號碼排到時一般都是在你之前已經沒有人了,所以你再去櫃檯辦理業務就不會被阻塞.
可見,同步/非同步與阻塞/非阻塞是兩組不同的概念,它們可以共存組合,

而很多人之所以把同步和阻塞混淆,我想也是因為沒有區分這兩個概念,
比如阻塞的read/write 操作中,其實是把訊息通知和處理訊息結合在了一起,
在這裡所關注的訊息就是fd 是否可讀/寫,而處理訊息則是對fd 讀/寫.
當我們將這個fd 設定為非阻塞的時候,read/write 操作就不會在等待訊息通知這裡阻塞,
如果fd 不可讀/寫則操作立即返回.

四、同步/非同步與阻塞/非阻塞的組合分析
_______阻塞____________________非阻塞_____
同步 | 同步阻塞              同步非阻塞
非同步 | 非同步阻塞              非同步非阻塞

同步阻塞形式:
  效率是最低的,
  拿上面的例子來說,就是你專心排隊,什麼別的事都不做。

  實際程式中
  就是未對fd 設定O_NONBLOCK 標誌位的read/write 操作,

非同步阻塞形式:
  如果在銀行等待辦理業務的人採用的是非同步的方式去等待訊息被觸發,也就是領了一張小紙條,
  假如在這段時間裡他不能離開銀行做其它的事情,那麼很顯然,這個人被阻塞在了這個等待的操作上面;

  非同步操作是可以被阻塞住的,只不過它不是在處理訊息時阻塞,而是在等待訊息被觸發時被阻塞.
  比如select 函式,
  假如傳入的最後一個timeout 引數為NULL,那麼如果所關注的事件沒有一個被觸發,
  程式就會一直阻塞在這個select 呼叫處.

同步非阻塞形式:
  實際上是效率低下的,
  想象一下你一邊打著電話一邊還需要抬頭看到底隊伍排到你了沒有,
  如果把打電話和觀察排隊的位置看成是程式的兩個操作的話,
  這個程式需要在這兩種不同的行為之間來回的切換,效率可想而知是低下的;

  很多人會寫阻塞的read/write 操作,
  但是別忘了可以對fd 設定O_NONBLOCK 標誌位,這樣就可以將同步操作變成非阻塞的了;

非同步非阻塞形式:
  效率更高,
  因為打電話是你(等待者)的事情,而通知你則是櫃檯(訊息觸發機制)的事情,
  程式沒有在兩種不同的操作中來回切換.

  比如說,這個人突然發覺自己煙癮犯了,需要出去抽根菸,
  於是他告訴大堂經理說,排到我這個號碼的時候麻煩到外面通知我一下(註冊一個回撥函式),
  那麼他就沒有被阻塞在這個等待的操作上面,自然這個就是非同步+非阻塞的方式了.

  如果使用非同步非阻塞的情況,
  比如aio_*組的操作,當發起一個aio_read 操作時,函式會馬上返回不會被阻塞,

  當所關注的事件被觸發時會呼叫之前註冊的回撥函式進行處理

 

以上淺薄的理解,是從我結合另一個部落格理解總結過來的,或許理解的不是很完善,以盡心盡力!

相關文章