轉:聊聊同步、異步、阻塞與非阻塞
轉載:https://www.jianshu.com/p/aed6067eeac9
近來遇到了一些常見的概念,尤其是網絡編程方面的概念,如:阻塞、非阻塞、異步I/O等等,對於這些概念自己也沒有太清晰的認識,只是很模糊的概念,說了解吧也了解,但是要讓自己準確的描述概念方面的具體細節,卻說的不那麽準確,這也是自己在這幾個方面也沒有細細考究過的原因吧。經過看了些這幾個概念的資料,發現同步、異步、阻塞、非阻塞的概念其實也並不難以理解,在此寫下此文,歡迎拍磚,希望多多交流。
1 同步與異步#
首先來解釋同步和異步的概念,這兩個概念與消息的通知機制有關。也就是同步與異步主要是從消息通知機制角度來說的。
1.1 概念描述##
所謂同步就是一個任務的完成需要依賴另外一個任務時,只有等待被依賴的任務完成後,依賴的任務才能算完成,這是一種可靠的任務序列
。要麽成功都成功,失敗都失敗,兩個任務的狀態可以保持一致。
所謂異步是不需要等待被依賴的任務完成,只是通知被依賴的任務要完成什麽工作,依賴的任務也立即執行,只要自己完成了整個任務就算完成了
。至於被依賴的任務最終是否真正完成,依賴它的任務無法確定,所以它是不可靠的任務序列
。
1.2 消息通知##
異步的概念和同步相對
。當一個同步調用發出後,調用者要一直等待返回消息(結果)通知後
,才能進行後續的執行;當一個異步過程調用發出後,調用者不能立刻得到返回消息(結果)。實際處理這個調用的部件在完成後,通過狀態、通知和回調來通知調用者
這裏提到執行部件和調用者通過三種途徑返回結果:狀態、通知和回調
。使用哪一種通知機制,依賴於執行部件的實現
,除非執行部件提供多種選擇,否則不受調用者控制
。
如果執行部件用狀態來通知,那麽調用者就需要每隔一定時間檢查一次,效率就很低(有些初學多線程編程的人,總喜歡用一個循環去檢查某個變量的值,這其實是一種很嚴重的錯誤);
如果是使用通知的方式,效率則很高,因為執行部件幾乎不需要做額外的操作。至於回調函數,其實和通知沒太多區別。
1.2 場景比喻##
舉個例子,比如我去銀行辦理業務,可能會有兩種方式:
選擇排隊等候;
另種選擇取一個小紙條上面有我的號碼,等到排到我這一號時由櫃臺的人通知我輪到我去辦理業務了;
第一種:前者(排隊等候)就是同步等待消息通知
,也就是我要一直在等待銀行辦理業務情況;
第二種:後者(等待別人通知)就是異步等待消息通知
。在異步消息處理中,等待消息通知者(在這個例子中就是等待辦理業務的人)往往註冊一個回調機制
,在所等待的事件被觸發時由觸發機制(在這裏是櫃臺的人)通過某種機制(在這裏是寫在小紙條上的號碼,喊號)找到等待該事件的人。
2 阻塞與非阻塞#
阻塞和非阻塞這兩個概念與程序(線程)等待消息通知(無所謂同步或者異步)時的狀態有關。也就是說阻塞與非阻塞主要是程序(線程)等待消息通知時的狀態角度來說的。
2.1 概念描述##
阻塞調用是指調用結果返回之前,當前線程會被掛起,一直處於等待消息通知,不能夠執行其他業務
。函數只有在得到結果之後才會返回。
有人也許會把阻塞調用和同步調用等同起來,實際上它們是不同的。
對於同步調用來說,很多時候當前線程可能還是激活的,只是從邏輯上當前函數沒有返回而已,此時,這個線程可能也會處理其他的消息
。還有一點,在這裏先擴展下:
(a) 如果這個線程在等待當前函數返回時,仍在執行其他消息處理,那這種情況就叫做同步非阻塞;
(b) 如果這個線程在等待當前函數返回時,沒有執行其他消息處理,而是處於掛起等待狀態,那這種情況就叫做同步阻塞;
所以同步的實現方式會有兩種:同步阻塞、同步非阻塞;同理,異步也會有兩種實現:異步阻塞、異步非阻塞;
- 對於阻塞調用來說,則當前線程就會被掛起等待當前函數返回;
非阻塞和阻塞的概念相對應,指在不能立刻得到結果之前,該函數不會阻塞當前線程,而會立刻返回
。雖然表面上看非阻塞的方式可以明顯的提高CPU的利用率,但是也帶了另外一種後果就是系統的線程切換增加
。增加的CPU執行時間能不能補償系統的切換成本需要好好評估
。
2.2 場景比喻##
繼續上面的那個例子,不論是排隊還是使用號碼等待通知,如果在這個等待的過程中,等待者除了等待消息通知之外不能做其它的事情,那麽該機制就是阻塞的
,表現在程序中,也就是該程序一直阻塞在該函數調用處不能繼續往下執行。
相反,有的人喜歡在銀行辦理這些業務的時候一邊打打電話發發短信一邊等待,這樣的狀態就是非阻塞的
,因為他(等待者)沒有阻塞在這個消息通知上,而是一邊做自己的事情一邊等待。
但是需要註意了,同步非阻塞形式實際上是效率低下的
,想象一下你一邊打著電話一邊還需要擡頭看到底隊伍排到你了沒有。如果把打電話和觀察排隊的位置看成是程序的兩個操作的話,這個程序需要在這兩種不同的行為之間來回的切換,效率可想而知是低下的;而異步非阻塞形式卻沒有這樣的問題
,因為打電話是你(等待者)的事情,而通知你則是櫃臺(消息觸發機制)的事情,程序沒有在兩種不同的操作中來回切換。
3 同步/異步與阻塞/非阻塞#
- 同步阻塞形式
效率是最低的,
拿上面的例子來說,就是你專心排隊,什麽別的事都不做。
實際程序中:就是未對fd 設置O_NONBLOCK標誌位的read/write 操作;
- 異步阻塞形式
如果在銀行等待辦理業務的人采用的是異步的方式去等待消息被觸發(通知)
,也就是領了一張小紙條,假如在這段時間裏他不能離開銀行做其它的事情,那麽很顯然,這個人被阻塞在了這個等待的操作上面;
異步操作是可以被阻塞住的,只不過它不是在處理消息時阻塞,而是在等待消息通知時被阻塞。
比如select 函數,假如傳入的最後一個timeout參數為NULL,那麽如果所關註的事件沒有一個被觸發,程序就會一直阻塞在這個select 調用處
。
- 同步非阻塞形式
實際上是效率低下的,
想象一下你一邊打著電話一邊還需要擡頭看到底隊伍排到你了沒有,如果把打電話和觀察排隊的位置看成是程序的兩個操作的話,這個程序需要在這兩種不同的行為之間來回的切換
,效率可想而知是低下的。
很多人會寫阻塞的read/write 操作,但是別忘了可以對fd設置O_NONBLOCK 標誌位,這樣就可以將同步操作變成非阻塞的了
。
- 異步非阻塞形式
效率更高,
因為打電話是你(等待者)的事情,而通知你則是櫃臺(消息觸發機制)的事情,程序沒有在兩種不同的操作中來回切換
。
比如說,這個人突然發覺自己煙癮犯了,需要出去抽根煙,於是他告訴大堂經理說,排到我這個號碼的時候麻煩到外面通知我一下(註冊一個回調函數),那麽他就沒有被阻塞在這個等待的操作上面,自然這個就是異步+非阻塞的方式了。
如果使用異步非阻塞的情況,比如aio_*組的操作,當發起一個aio_read操作時,函數會馬上返回不會被阻塞,當所關註的事件被觸發時會調用之前註冊的回調函數進行處理
。
很多人會把同步和阻塞混淆,我想是因為很多時候同步操作會以阻塞的形式表現出來
,比如很多人會寫阻塞的read/write操作,但是別忘了可以對fd設置O_NONBLOCK標誌位,這樣就可以將同步操作變成非阻塞的了。但最根本是因為沒有區分這兩個概念
,比如阻塞的read/write操作中,其實是把消息通知機制和等待消息通知的狀態結合在了一起
,在這裏所關註的消息就是fd是否可讀/寫
,而等待消息通知的狀態則是對fd可讀/寫等待過程中程序(線程)的狀態
。當我們將這個fd設置為非阻塞的時候,read/write操作就不會在等待消息通知這裏阻塞,如果fd不可讀/寫則操作立即返回。
同樣的,很多人也會把異步和非阻塞混淆,因為異步操作一般都不會在真正的IO操作處被阻塞
,比如如果用select函數,當select返回可讀時再去read一般都不會被阻塞,而是在select函數調用處阻塞
。
4 小明的故事#
對上面所講的概念再次進行一個場景梳理,上面已經明確說明,同步/異步關註的是消息通知的機制,而阻塞/非阻塞關註的是程序(線程)等待消息通知時的狀態
。以小明下載文件打個比方,從這兩個關註點來再次說明這兩組概念,希望能夠更好的促進大家的理解。
- 同步阻塞:小明一直盯著下載進度條,到 100% 的時候就完成。
同步體現在:等待下載完成通知;
阻塞體現在:等待下載完成通知過程中,不能做其他任務處理;
- 同步非阻塞:小明提交下載任務後就去幹別的,每過一段時間就去瞄一眼進度條,看到 100% 就完成。
同步體現在:等待下載完成通知;
非阻塞體現在:等待下載完成通知過程中,去幹別的任務了,只是時不時會瞄一眼進度條;【小明必須要在兩個任務間切換,關註下載進度】
- 異步阻塞:小明換了個有下載完成通知功能的軟件,下載完成就“叮”一聲。不過小明仍然一直等待“叮”的聲音(看起來很傻,不是嗎)。
異步體現在:下載完成“叮”一聲通知;
阻塞體現在:等待下載完成“叮”一聲通知過程中,不能做其他任務處理;
- 異步非阻塞:仍然是那個會“叮”一聲的下載軟件,小明提交下載任務後就去幹別的,聽到“叮”的一聲就知道完成了。
異步體現在:下載完成“叮”一聲通知;
非阻塞體現在:等待下載完成“叮”一聲通知過程中,去幹別的任務了,只需要接收“叮”聲通知即可;【軟件處理下載任務,小明處理其他任務,不需關註進度,只需接收軟件“叮”聲通知,即可】
也就是說,同步/異步是“下載完成消息”通知的方式(機制),而阻塞/非阻塞則是在等待“下載完成消息”通知過程中的狀態(能不能幹其他任務)
,在不同的場景下,同步/異步、阻塞/非阻塞的四種組合都有應用。
所以,綜上所述,同步和異步僅僅是關註的消息如何通知的機制,而阻塞與非阻塞關註的是等待消息通知時的狀態
。也就是說,同步的情況下,是由處理消息者自己去等待消息是否被觸發,而異步的情況下是由觸發機制來通知處理消息者
,所以在異步機制中,處理消息者和觸發機制之間就需要一個連接的橋梁
:
在銀行的例子中,這個橋梁就是小紙條上面的號碼。
在小明的例子中,這個橋梁就是軟件“叮”的聲音。
最後,請大家註意理解“消息通知機制”和“等待消息通知時的狀態”這兩個概念,這是理解四個概念的關鍵所在。
作者:書海陶然
鏈接:https://www.jianshu.com/p/aed6067eeac9
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請註明出處。
轉:聊聊同步、異步、阻塞與非阻塞