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