來(lái)源:伯樂(lè)在線專(zhuān)欄作者 - 陶邦仁
鏈接:http://blog.jobbole.com/99765/
近來(lái)遇到了一些常見(jiàn)的概念终惑,尤其是網(wǎng)絡(luò)編程方面的概念,如:阻塞门扇、非阻塞雹有、異步I/O等等,對(duì)于這些概念自己也沒(méi)有太清晰的認(rèn)識(shí)臼寄,只是很模糊的概念件舵,說(shuō)了解吧也了解,但是要讓自己準(zhǔn)確的描述概念方面的具體細(xì)節(jié)脯厨,卻說(shuō)的不那么準(zhǔn)確铅祸,這也是自己在這幾個(gè)方面也沒(méi)有細(xì)細(xì)考究過(guò)的原因吧。經(jīng)過(guò)看了些這幾個(gè)概念的資料,發(fā)現(xiàn)同步临梗、異步涡扼、阻塞、非阻塞的概念其實(shí)也并不難以理解盟庞,在此寫(xiě)下此文吃沪,歡迎拍磚,希望多多交流什猖。
1 同步與異步
首先來(lái)解釋同步和異步的概念票彪,這兩個(gè)概念與消息的通知機(jī)制有關(guān)。也就是同步與異步主要是從消息通知機(jī)制角度來(lái)說(shuō)的不狮。
1.1 概念描述
所謂同步就是一個(gè)任務(wù)的完成需要依賴(lài)另外一個(gè)任務(wù)時(shí)降铸,只有等待被依賴(lài)的任務(wù)完成后,依賴(lài)的任務(wù)才能算完成摇零,這是一種可靠的任務(wù)序列推掸。要么成功都成功,失敗都失敗驻仅,兩個(gè)任務(wù)的狀態(tài)可以保持一致谅畅。
所謂異步是不需要等待被依賴(lài)的任務(wù)完成,只是通知被依賴(lài)的任務(wù)要完成什么工作噪服,依賴(lài)的任務(wù)也立即執(zhí)行毡泻,只要自己完成了整個(gè)任務(wù)就算完成了。至于被依賴(lài)的任務(wù)最終是否真正完成粘优,依賴(lài)它的任務(wù)無(wú)法確定牙捉,所以它是不可靠的任務(wù)序列。
1.2 消息通知
異步的概念和同步相對(duì)敬飒。當(dāng)一個(gè)同步調(diào)用發(fā)出后邪铲,調(diào)用者要一直等待返回消息(結(jié)果)通知后,才能進(jìn)行后續(xù)的執(zhí)行无拗;當(dāng)一個(gè)異步過(guò)程調(diào)用發(fā)出后带到,調(diào)用者不能立刻得到返回消息(結(jié)果)。實(shí)際處理這個(gè)調(diào)用的部件在完成后英染,通過(guò)狀態(tài)揽惹、通知和回調(diào)來(lái)通知調(diào)用者。
這里提到執(zhí)行部件和調(diào)用者通過(guò)三種途徑返回結(jié)果:狀態(tài)四康、通知和回調(diào)搪搏。使用哪一種通知機(jī)制,依賴(lài)于執(zhí)行部件的實(shí)現(xiàn)闪金,除非執(zhí)行部件提供多種選擇疯溺,否則不受調(diào)用者控制论颅。
如果執(zhí)行部件用狀態(tài)來(lái)通知,那么調(diào)用者就需要每隔一定時(shí)間檢查一次囱嫩,效率就很低(有些初學(xué)多線程編程的人恃疯,總喜歡用一個(gè)循環(huán)去檢查某個(gè)變量的值,這其實(shí)是一種很?chē)?yán)重的錯(cuò)誤)墨闲;
如果是使用通知的方式今妄,效率則很高,因?yàn)閳?zhí)行部件幾乎不需要做額外的操作鸳碧。至于回調(diào)函數(shù)盾鳞,其實(shí)和通知沒(méi)太多區(qū)別。
1.3 場(chǎng)景比喻
舉個(gè)例子瞻离,比如我去銀行辦理業(yè)務(wù)腾仅,可能會(huì)有兩種方式:
選擇排隊(duì)等候;
另種選擇取一個(gè)小紙條上面有我的號(hào)碼琐脏,等到排到我這一號(hào)時(shí)由柜臺(tái)的人通知我輪到我去辦理業(yè)務(wù)了;
第一種:前者(排隊(duì)等候)就是同步等待消息通知缸兔,也就是我要一直在等待銀行辦理業(yè)務(wù)情況日裙;
第二種:后者(等待別人通知)就是異步等待消息通知。在異步消息處理中惰蜜,等待消息通知者(在這個(gè)例子中就是等待辦理業(yè)務(wù)的人)往往注冊(cè)一個(gè)回調(diào)機(jī)制昂拂,在所等待的事件被觸發(fā)時(shí)由觸發(fā)機(jī)制(在這里是柜臺(tái)的人)通過(guò)某種機(jī)制(在這里是寫(xiě)在小紙條上的號(hào)碼,喊號(hào))找到等待該事件的人抛猖。
2 阻塞與非阻塞
阻塞和非阻塞這兩個(gè)概念與程序(線程)等待消息通知(無(wú)所謂同步或者異步)時(shí)的狀態(tài)有關(guān)格侯。也就是說(shuō)阻塞與非阻塞主要是程序(線程)等待消息通知時(shí)的狀態(tài)角度來(lái)說(shuō)的。
2.1 概念描述
阻塞調(diào)用是指調(diào)用結(jié)果返回之前财著,當(dāng)前線程會(huì)被掛起联四,一直處于等待消息通知,不能夠執(zhí)行其他業(yè)務(wù)撑教。函數(shù)只有在得到結(jié)果之后才會(huì)返回朝墩。
有人也許會(huì)把阻塞調(diào)用和同步調(diào)用等同起來(lái),實(shí)際上它們是不同的伟姐。
對(duì)于同步調(diào)用來(lái)說(shuō)收苏,很多時(shí)候當(dāng)前線程可能還是激活的,只是從邏輯上當(dāng)前函數(shù)沒(méi)有返回而已愤兵,此時(shí)鹿霸,這個(gè)線程可能也會(huì)處理其他的消息。還有一點(diǎn)秆乳,在這里先擴(kuò)展下:
如果這個(gè)線程在等待當(dāng)前函數(shù)返回時(shí)懦鼠,仍在執(zhí)行其他消息處理,那這種情況就叫做同步非阻塞;
如果這個(gè)線程在等待當(dāng)前函數(shù)返回時(shí)葛闷,沒(méi)有執(zhí)行其他消息處理憋槐,而是處于掛起等待狀態(tài),那這種情況就叫做同步阻塞淑趾;
所以同步的實(shí)現(xiàn)方式會(huì)有兩種:同步阻塞阳仔、同步非阻塞;同理扣泊,異步也會(huì)有兩種實(shí)現(xiàn):異步阻塞近范、異步非阻塞;
對(duì)于阻塞調(diào)用來(lái)說(shuō)延蟹,則當(dāng)前線程就會(huì)被掛起等待當(dāng)前函數(shù)返回评矩;
非阻塞和阻塞的概念相對(duì)應(yīng),指在不能立刻得到結(jié)果之前阱飘,該函數(shù)不會(huì)阻塞當(dāng)前線程斥杜,而會(huì)立刻返回。雖然表面上看非阻塞的方式可以明顯的提高CPU的利用率沥匈,但是也帶了另外一種后果就是系統(tǒng)的線程切換增加蔗喂。增加的CPU執(zhí)行時(shí)間能不能補(bǔ)償系統(tǒng)的切換成本需要好好評(píng)估。
2.2 場(chǎng)景比喻
繼續(xù)上面的那個(gè)例子高帖,不論是排隊(duì)還是使用號(hào)碼等待通知缰儿,如果在這個(gè)等待的過(guò)程中,等待者除了等待消息通知之外不能做其它的事情散址,那么該機(jī)制就是阻塞的乖阵,表現(xiàn)在程序中,也就是該程序一直阻塞在該函數(shù)調(diào)用處不能繼續(xù)往下執(zhí)行。
相反预麸,有的人喜歡在銀行辦理這些業(yè)務(wù)的時(shí)候一邊打打電話發(fā)發(fā)短信一邊等待瞪浸,這樣的狀態(tài)就是非阻塞的,因?yàn)樗?等待者)沒(méi)有阻塞在這個(gè)消息通知上吏祸,而是一邊做自己的事情一邊等待默终。
但是需要注意了,同步非阻塞形式實(shí)際上是效率低下的犁罩,想象一下你一邊打著電話一邊還需要抬頭看到底隊(duì)伍排到你了沒(méi)有齐蔽。如果把打電話和觀察排隊(duì)的位置看成是程序的兩個(gè)操作的話,這個(gè)程序需要在這兩種不同的行為之間來(lái)回的切換床估,效率可想而知是低下的含滴;而異步非阻塞形式卻沒(méi)有這樣的問(wèn)題,因?yàn)榇螂娫捠悄?等待者)的事情丐巫,而通知你則是柜臺(tái)(消息觸發(fā)機(jī)制)的事情谈况,程序沒(méi)有在兩種不同的操作中來(lái)回切換勺美。
3 同步/異步與阻塞/非阻塞
同步阻塞形式效率是最低的,
拿上面的例子來(lái)說(shuō)碑韵,就是你專(zhuān)心排隊(duì)赡茸,什么別的事都不做。
實(shí)際程序中:就是未對(duì)fd 設(shè)置O_NONBLOCK標(biāo)志位的read/write 操作祝闻;
異步阻塞形式如果在銀行等待辦理業(yè)務(wù)的人采用的是異步的方式去等待消息被觸發(fā)(通知)占卧,也就是領(lǐng)了一張小紙條,假如在這段時(shí)間里他不能離開(kāi)銀行做其它的事情联喘,那么很顯然华蜒,這個(gè)人被阻塞在了這個(gè)等待的操作上面;
異步操作是可以被阻塞住的豁遭,只不過(guò)它不是在處理消息時(shí)阻塞叭喜,而是在等待消息通知時(shí)被阻塞。
比如select 函數(shù)蓖谢,假如傳入的最后一個(gè)timeout參數(shù)為NULL捂蕴,那么如果所關(guān)注的事件沒(méi)有一個(gè)被觸發(fā),程序就會(huì)一直阻塞在這個(gè)select 調(diào)用處闪幽。
同步非阻塞形式實(shí)際上是效率低下的
想象一下你一邊打著電話一邊還需要抬頭看到底隊(duì)伍排到你了沒(méi)有啥辨,如果把打電話和觀察排隊(duì)的位置看成是程序的兩個(gè)操作的話,這個(gè)程序需要在這兩種不同的行為之間來(lái)回的切換沟使,效率可想而知是低下的委可。
很多人會(huì)寫(xiě)阻塞的read/write 操作渊跋,但是別忘了可以對(duì)fd設(shè)置O_NONBLOCK 標(biāo)志位腊嗡,這樣就可以將同步操作變成非阻塞的了。
異步非阻塞形式效率更高拾酝,
因?yàn)榇螂娫捠悄?等待者)的事情燕少,而通知你則是柜臺(tái)(消息觸發(fā)機(jī)制)的事情,程序沒(méi)有在兩種不同的操作中來(lái)回切換蒿囤。
比如說(shuō)客们,這個(gè)人突然發(fā)覺(jué)自己煙癮犯了,需要出去抽根煙材诽,于是他告訴大堂經(jīng)理說(shuō)底挫,排到我這個(gè)號(hào)碼的時(shí)候麻煩到外面通知我一下(注冊(cè)一個(gè)回調(diào)函數(shù)),那么他就沒(méi)有被阻塞在這個(gè)等待的操作上面脸侥,自然這個(gè)就是異步+非阻塞的方式了建邓。
如果使用異步非阻塞的情況,比如aio_*組的操作睁枕,當(dāng)發(fā)起一個(gè)aio_read操作時(shí)官边,函數(shù)會(huì)馬上返回不會(huì)被阻塞沸手,當(dāng)所關(guān)注的事件被觸發(fā)時(shí)會(huì)調(diào)用之前注冊(cè)的回調(diào)函數(shù)進(jìn)行處理。
很多人會(huì)把同步和阻塞混淆注簿,我想是因?yàn)楹芏鄷r(shí)候同步操作會(huì)以阻塞的形式表現(xiàn)出來(lái)契吉,比如很多人會(huì)寫(xiě)阻塞的read/write操作,但是別忘了可以對(duì)fd設(shè)置O_NONBLOCK標(biāo)志位诡渴,這樣就可以將同步操作變成非阻塞的了捐晶。但最根本是因?yàn)闆](méi)有區(qū)分這兩個(gè)概念,比如阻塞的read/write操作中玩徊,其實(shí)是把消息通知機(jī)制和等待消息通知的狀態(tài)結(jié)合在了一起租悄,在這里所關(guān)注的消息就是fd是否可讀/寫(xiě),而等待消息通知的狀態(tài)則是對(duì)fd可讀/寫(xiě)等待過(guò)程中程序(線程)的狀態(tài)恩袱。當(dāng)我們將這個(gè)fd設(shè)置為非阻塞的時(shí)候泣棋,read/write操作就不會(huì)在等待消息通知這里阻塞,如果fd不可讀/寫(xiě)則操作立即返回畔塔。
同樣的潭辈,很多人也會(huì)把異步和非阻塞混淆,因?yàn)楫惒讲僮饕话愣疾粫?huì)在真正的IO操作處被阻塞澈吨,比如如果用select函數(shù)把敢,當(dāng)select返回可讀時(shí)再去read一般都不會(huì)被阻塞,而是在select函數(shù)調(diào)用處阻塞谅辣。
4 小明的故事
對(duì)上面所講的概念再次進(jìn)行一個(gè)場(chǎng)景梳理修赞,上面已經(jīng)明確說(shuō)明,同步/異步關(guān)注的是消息通知的機(jī)制桑阶,而阻塞/非阻塞關(guān)注的是程序(線程)等待消息通知時(shí)的狀態(tài)柏副。以小明下載文件打個(gè)比方,從這兩個(gè)關(guān)注點(diǎn)來(lái)再次說(shuō)明這兩組概念蚣录,希望能夠更好的促進(jìn)大家的理解割择。
同步阻塞:小明一直盯著下載進(jìn)度條,到 100% 的時(shí)候就完成萎河。
同步體現(xiàn)在:等待下載完成通知荔泳;
阻塞體現(xiàn)在:等待下載完成通知過(guò)程中,不能做其他任務(wù)處理虐杯;
同步非阻塞:小明提交下載任務(wù)后就去干別的玛歌,每過(guò)一段時(shí)間就去瞄一眼進(jìn)度條,看到 100% 就完成擎椰。
同步體現(xiàn)在:等待下載完成通知支子;
非阻塞體現(xiàn)在:等待下載完成通知過(guò)程中,去干別的任務(wù)了确憨,只是時(shí)不時(shí)會(huì)瞄一眼進(jìn)度條译荞;【小明必須要在兩個(gè)任務(wù)間切換瓤的,關(guān)注下載進(jìn)度】
異步阻塞:小明換了個(gè)有下載完成通知功能的軟件,下載完成就“锻碳撸”一聲圈膏。不過(guò)小明仍然一直等待“叮”的聲音(看起來(lái)很傻篙骡,不是嗎)稽坤。
異步體現(xiàn)在:下載完成“叮”一聲通知糯俗;
阻塞體現(xiàn)在:等待下載完成“赌蛲剩”一聲通知過(guò)程中,不能做其他任務(wù)處理得湘;
異步非阻塞:仍然是那個(gè)會(huì)“墩攘幔”一聲的下載軟件,小明提交下載任務(wù)后就去干別的淘正,聽(tīng)到“栋诼恚”的一聲就知道完成了。
異步體現(xiàn)在:下載完成“逗柽海”一聲通知囤采;
非阻塞體現(xiàn)在:等待下載完成“叮”一聲通知過(guò)程中惩淳,去干別的任務(wù)了蕉毯,只需要接收“叮”聲通知即可思犁;【軟件處理下載任務(wù)代虾,小明處理其他任務(wù),不需關(guān)注進(jìn)度抒倚,只需接收軟件“逗肿牛”聲通知坷澡,即可】
也就是說(shuō)托呕,同步/異步是“下載完成消息”通知的方式(機(jī)制),而阻塞/非阻塞則是在等待“下載完成消息”通知過(guò)程中的狀態(tài)(能不能干其他任務(wù))频敛,在不同的場(chǎng)景下项郊,同步/異步、阻塞/非阻塞的四種組合都有應(yīng)用斟赚。
所以着降,綜上所述,同步和異步僅僅是關(guān)注的消息如何通知的機(jī)制拗军,而阻塞與非阻塞關(guān)注的是等待消息通知時(shí)的狀態(tài)任洞。也就是說(shuō)蓄喇,同步的情況下,是由處理消息者自己去等待消息是否被觸發(fā)交掏,而異步的情況下是由觸發(fā)機(jī)制來(lái)通知處理消息者妆偏,所以在異步機(jī)制中,處理消息者和觸發(fā)機(jī)制之間就需要一個(gè)連接的橋梁:
在銀行的例子中盅弛,這個(gè)橋梁就是小紙條上面的號(hào)碼钱骂。
在小明的例子中,這個(gè)橋梁就是軟件“杜才簦”的聲音见秽。
最后,請(qǐng)大家注意理解“消息通知機(jī)制”和“等待消息通知時(shí)的狀態(tài)”這兩個(gè)概念讨盒,這是理解四個(gè)概念的關(guān)鍵所在解取。
專(zhuān)欄作者簡(jiǎn)介(點(diǎn)擊 → 加入專(zhuān)欄作者)
陶邦仁:專(zhuān)注于后端技術(shù)研究,前端技術(shù)略有涉獵返顺,熱衷于構(gòu)建高性能肮蛹、高可用網(wǎng)站,對(duì)平臺(tái)服務(wù)化创南、分布式服務(wù)伦忠、分布式存儲(chǔ)等方面的解決方案。目前就職于千丁互聯(lián)稿辙,任技術(shù)經(jīng)理一職昆码,負(fù)責(zé)社區(qū)產(chǎn)品技術(shù)研發(fā)。曾就職于京東邻储,負(fù)責(zé)庫(kù)存組緩存方案技術(shù)實(shí)現(xiàn)赋咽;曾就職于百度糯米,負(fù)責(zé)PC首頁(yè)吨娜、APP個(gè)性化排單服務(wù)化解決方案脓匿。