對於同步、非同步、阻塞、非阻塞的幾點淺薄理
- 2020 年 1 月 19 日
- 筆記
首先我們先來看看:
一、同步與非同步 同步/非同步, 它們是消息的通知機制(都是通過狀態、通知、回調函數來返回結果) 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 操作時,函數會馬上返回不會被阻塞,
當所關注的事件被觸發時會調用之前註冊的回調函數進行處理
以上淺薄的理解,是從我結合另一個部落格理解總結過來的,或許理解的不是很完善,以盡心儘力!