程序员修神之路--问世间异步为何物?
菜菜哥,今天天氣挺熱的,我都穿裙子了
說(shuō)吧,什么事??
苦笑一下..... 老大說(shuō)把所有的接口都改成異步操作
異步好呀,最少比同步能提高吞吐量
異步是怎么回事呢,能講講不?
來(lái),湊近一點(diǎn),哥給你解釋一番
◆◆異步定義◆◆關(guān)于異步的定義,網(wǎng)上有很多不同的形式,但是歸根結(jié)底中心思想是不變的。無(wú)論是在http請(qǐng)求調(diào)用的層面,還是在cpu內(nèi)核態(tài)和用戶態(tài)傳輸數(shù)據(jù)的層面,異步這個(gè)行為針對(duì)的是調(diào)用方:
在多數(shù)程序員的概念中一般是指線程處理的層面:
異步是計(jì)算機(jī)多線程的異步處理。與同步處理相對(duì),異步處理不用阻塞當(dāng)前線程來(lái)等待處理完成,而是允許后續(xù)操作,直至其它線程將處理完成,并回調(diào)通知此線程可以這樣通俗的理解,異步主要解決的問(wèn)題是不阻塞調(diào)用方,用方這里可以是http請(qǐng)求的發(fā)起者,也可以是一個(gè)線程。
但此處需要明確的是:異步與多線程與并行不是同一個(gè)概念。
我聽(tīng)有的同學(xué)說(shuō),異步解決的是IO密集型的操作,菜菜覺(jué)得是不準(zhǔn)確的。異步同樣可以解決CPU密集型操作,只不過(guò)場(chǎng)景有限而已。有一個(gè)前提:利用異步解決CPU密集型操作要求當(dāng)前運(yùn)行環(huán)境支持多線程才行,比如javascript這個(gè)語(yǔ)言,本質(zhì)上它的運(yùn)行環(huán)境是單線程的,所以對(duì)于CPU密集型操作,javascript會(huì)顯得力不從心。
異步解決CPU密集操作一般情況下發(fā)生在同進(jìn)程中,為什么這么說(shuō)呢,如果發(fā)生在不同機(jī)器或者不同進(jìn)程在很多情況下已經(jīng)屬于IO密集型的范圍了。這里順便提醒一下:IO操作可不單單是指磁盤的操作,所有有輸入/輸出(Input/Output)操作的都可以泛稱為IO。
舉個(gè)栗子吧:
在一個(gè)帶有UI的軟件上點(diǎn)擊一個(gè)按鈕,UI線程會(huì)發(fā)生操作行為,假如UI線程在執(zhí)行過(guò)程中有一個(gè)計(jì)算比較耗時(shí)的操作(你可以想象成計(jì)算1--999999999的和),UI線程在同步操作的情況下會(huì)一直等待計(jì)算結(jié)果,在計(jì)算完畢之后才會(huì)繼續(xù)執(zhí)行剩余操作,在等待的這個(gè)過(guò)程中,呈現(xiàn)給用戶的情況就是UI卡住了,俗稱假死了,帶給用戶的體驗(yàn)是非常不好的。這種情況下,我們可以新啟動(dòng)一個(gè)線程去執(zhí)行這個(gè)耗時(shí)的操作,當(dāng)執(zhí)行完畢,利用某種通知機(jī)制來(lái)通知原來(lái)線程,以便原來(lái)線程繼續(xù)自己的操作。
異步的優(yōu)勢(shì)在IO密集型操作中表現(xiàn)的淋漓盡致,無(wú)論是讀取一個(gè)文件還是發(fā)起一個(gè)網(wǎng)絡(luò)請(qǐng)求,菜菜的建議是盡量使用異步。這里首先普及一個(gè)小知識(shí):其實(shí)每個(gè)外設(shè)設(shè)備都有自己的處理器,比如磁盤,所以每個(gè)外設(shè)設(shè)備都可以處理自己相應(yīng)的請(qǐng)求操作。但是處理外設(shè)設(shè)備信息的速度和cpu的執(zhí)行速度來(lái)比較有著天壤之別。
上圖展示了不同的?IO?操作所占用的?CPU?時(shí)鐘周期,在計(jì)算機(jī)中,CPU?的運(yùn)算速度最快,以其的運(yùn)算速度為基準(zhǔn),時(shí)鐘周期為1。其次是一級(jí)緩存、二級(jí)緩存和內(nèi)存,硬盤和網(wǎng)絡(luò)最慢,它們所花費(fèi)的時(shí)鐘周期和內(nèi)存所花費(fèi)的時(shí)鐘周期差距在五位數(shù)以上,更不用提跟?CPU?和一級(jí)緩存、二級(jí)緩存的差距了。
由于速度的差距,所以幾乎所有的IO操作都推薦使用異步。比如當(dāng)讀取磁盤一個(gè)文件的時(shí)候,同步狀態(tài)下當(dāng)前線程在等待讀取的結(jié)果,這個(gè)線程閑置的時(shí)間幾乎可以用蛋疼來(lái)形容。所以現(xiàn)代的幾乎所有的知名第三方的操作都是異步操作,尤其以Redis,Nodejs?為代表的單線程運(yùn)行環(huán)境令人刮目相看。
現(xiàn)在是微服務(wù)盛行的時(shí)代,UI往往一個(gè)簡(jiǎn)單的按鈕操作,其實(shí)在后臺(tái)程序可能調(diào)用了幾個(gè)甚至更多的微服務(wù)接口(關(guān)于微服務(wù)這里不展開(kāi)),如果程序是同步操作的話,那響應(yīng)時(shí)間是這些服務(wù)接口響應(yīng)時(shí)間的和,但是如果采用的是異步操作,調(diào)用方可以在瞬間把調(diào)用服務(wù)接口的操作發(fā)送出去,線程可以繼續(xù)執(zhí)行下邊代碼或者等待所有的服務(wù)接口返回值也可以。最差的情況下,接口的響應(yīng)時(shí)間為最慢的那個(gè)服務(wù)接口響應(yīng)時(shí)間,這有點(diǎn)類似于木桶效應(yīng)。
通過(guò)以上介紹,我們一定要記住一個(gè)知識(shí)點(diǎn):異步需要回調(diào)機(jī)制。異步操作之所以能在執(zhí)行結(jié)果完成之后繼續(xù)執(zhí)行下面程序完全歸功于回調(diào),這也是所有異步場(chǎng)景的核心所在,前到j(luò)s的異步回調(diào),后到cpu內(nèi)核空間copy數(shù)據(jù)到用戶空間完成通知?等等異步場(chǎng)景,回調(diào)無(wú)處不在。說(shuō)道回調(diào)大部分語(yǔ)言都是注冊(cè)一個(gè)回調(diào)函數(shù),比如js會(huì)把回調(diào)的方法注冊(cè)到執(zhí)行的隊(duì)列,c#會(huì)把回調(diào)注冊(cè)到IOCP。這里延伸一下,在很多系統(tǒng)里,很多IO網(wǎng)絡(luò)模型其實(shí)是屬于同步范疇的,比如多路復(fù)用技術(shù),真正異步非阻塞的推薦windows下的IOCP。
現(xiàn)在很多現(xiàn)代語(yǔ)言都支持更優(yōu)秀的回調(diào)方式,比如js和c#?現(xiàn)在都支持async?和await方式來(lái)進(jìn)行異步操作。
優(yōu)勢(shì)
1異步操作無(wú)須額外的線程負(fù)擔(dān),使用回調(diào)的方式進(jìn)行后續(xù)處理,在設(shè)計(jì)良好的情況下,處理函數(shù)可以不必使用共享變量(即使無(wú)法完全不用,最起碼可以減少?共享變量的數(shù)量),減少了死鎖的可能。
2線程數(shù)量的減少,減少了線程上下文在cpu切換的開(kāi)銷。
3微服務(wù)環(huán)境(調(diào)用多個(gè)服務(wù)接口的情況下)加快了上層接口的響應(yīng)時(shí)間,意味著增加了上層接口的吞吐量
劣勢(shì)
1異步操作傳統(tǒng)的做法都是通過(guò)回調(diào)函數(shù)來(lái)實(shí)現(xiàn),與同步的思維有些差異,而且難以調(diào)試2如果當(dāng)前環(huán)境有操作順序的要求,異步操作為了保證執(zhí)行的順序需要做額外的工作3由于多數(shù)情況下異步的回調(diào)過(guò)程中的執(zhí)行線程并非原來(lái)的線程,所以在捕獲異常,上下文傳遞等方面需要做特殊處理,特別是不同線程共享代碼或共享數(shù)據(jù)時(shí)容易出問(wèn)題。寫在最后1在并發(fā)量較小的情況下,阻塞式?IO和異步IO的差距可能不是那么明顯,但隨著并發(fā)量的增加,異步IO的優(yōu)勢(shì)將會(huì)越來(lái)越大,吞吐率和性能上的差距也會(huì)越來(lái)越明顯。
2在壓力比較小的情況下,一般異步請(qǐng)求的響應(yīng)時(shí)間大于同步請(qǐng)求的響應(yīng)時(shí)間,因?yàn)楫惒降幕卣{(diào)也是需要時(shí)間的
3在大并發(fā)的情況下,采用異步調(diào)用的程序所用線程數(shù)要遠(yuǎn)遠(yuǎn)小于同步調(diào)用程序所用的線程數(shù),cpu使用率也一樣(因?yàn)楸苊饬颂嗑€程上下文切換的成本)
為了系統(tǒng)性能,不要讓任何設(shè)備停下來(lái)休息
互聯(lián)網(wǎng)之路,菜菜與君一同成長(zhǎng)
長(zhǎng)按識(shí)別二維碼關(guān)注
你點(diǎn)的每個(gè)在看,我都認(rèn)真當(dāng)成了喜總結(jié)
以上是生活随笔為你收集整理的程序员修神之路--问世间异步为何物?的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: .NET 泛型,详细介绍
- 下一篇: .net core 使用RSA获取私钥证