程式設計師修神之路--問世間非同步為何物?
非同步定義
關於非同步的定義,網上有很多不同的形式,但是歸根結底中心思想是不變的。無論是在http請求呼叫的層面,還是在cpu核心態和使用者態傳輸資料的層面,非同步這個行為針對的是呼叫方:
一個可以無需等待被呼叫方的返回值就讓操作繼續進行的方法
在多數程式設計師的概念中一般是指執行緒處理的層面:
非同步是計算機多執行緒的非同步處理。與同步處理相對,非同步處理不用阻塞當前執行緒來等待處理完成,而是允許後續操作,直至其它執行緒將處理完成,並回撥通知此執行緒
可以這樣通俗的理解,非同步主要解決的問題是不阻塞呼叫方,用方這裡可以是http請求的發起者,也可以是一個執行緒。
但此處需要明確的是:非同步與多執行緒與並行不是同一個概念。
CPU密集型操作
我聽有的同學說,非同步解決的是IO密集型的操作,菜菜覺得是不準確的。非同步同樣可以解決CPU密集型操作,只不過場景有限而已。有一個前提:利用非同步解決CPU密集型操作要求當前執行環境支援多執行緒才行,比如javascript這個語言,本質上它的執行環境是單執行緒的,所以對於CPU密集型操作,javascript會顯得力不從心。
非同步解決CPU密集操作一般情況下發生在同程式中,為什麼這麼說呢,如果發生在不同機器或者不同程式在很多情況下已經屬於IO密集型的範圍了。這裡順便提醒一下:IO操作可不單單是指磁碟的操作,所有有輸入/輸出(Input/Output)操作的都可以泛稱為IO。
舉個 栗子吧:
在一個帶有UI的軟體上點選一個按鈕,UI執行緒會發生操作行為,假如UI執行緒在執行過程中有一個計算比較耗時的操作(你可以想象成計算1--999999999的和),UI執行緒在同步操作的情況下會一直等待計算結果,在計算完畢之後才會繼續執行剩餘操作,在等待的這個過程中,呈現給使用者的情況就是UI卡住了,俗稱假死了,帶給使用者的體驗是非常不好的。這種情況下,我們可以新啟動一個執行緒去執行這個耗時的操作,當執行完畢,利用某種通知機制來通知原來執行緒,以便原來執行緒繼續自己的操作。
啟動新執行緒執行CPU密集型操作利用的其實就是多執行緒的優勢,如果是單核CPU,其實這種優勢並不明顯
IO密集型操作
非同步的優勢在IO密集型操作中表現的淋漓盡致,無論是讀取一個檔案還是發起一個網路請求,菜菜的建議是儘量使用非同步。這裡首先普及一個小知識:其實每個外設裝置都有自己的處理器,比如磁碟,所以每個外設裝置都可以處理自己相應的請求操作。但是處理外設裝置資訊的速度和cpu的執行速度來比較有著天壤之別。
上圖展示了不同的 IO 操作所佔用的 CPU 時鐘週期,在計算機中,CPU 的運算速度最快,以其的運算速度為基準,時鐘週期為1。其次是一級快取、二級快取和記憶體,硬碟和網路最慢,它們所花費的時鐘週期和記憶體所花費的時鐘週期差距在五位數以上,更不用提跟 CPU 和一級快取、二級快取的差距了。
由於速度的差距,所以幾乎所有的IO操作都推薦使用非同步。比如當讀取磁碟一個檔案的時候,同步狀態下當前執行緒在等待讀取的結果,這個執行緒閒置的時間幾乎可以用蛋疼來形容。所以現代的幾乎所有的知名第三方的操作都是非同步操作,尤其以Redis,Nodejs 為代表的單執行緒執行環境令人刮目相看。
現在是微服務盛行的時代,UI往往一個簡單的按鈕操作,其實在後臺程式可能呼叫了幾個甚至更多的微服務介面(關於微服務這裡不展開),如果程式是同步操作的話,那響應時間是這些服務介面響應時間的和,但是如果採用的是非同步操作,呼叫方可以在瞬間把呼叫服務介面的操作傳送出去,執行緒可以繼續執行下邊程式碼或者等待所有的服務介面返回值也可以。最差的情況下,介面的響應時間為最慢的那個服務介面響應時間,這有點類似於木桶效應。
非同步的回撥
透過以上介紹,我們一定要記住一個知識點:非同步需要回撥機制。非同步操作之所以能在執行結果完成之後繼續執行下面程式完全歸功於回撥,這也是所有非同步場景的核心所在,前到js的非同步回撥,後到cpu核心空間copy資料到使用者空間完成通知 等等非同步場景,回撥無處不在。說道回撥大部分語言都是註冊一個回撥函式,比如js會把回撥的方法註冊到執行的佇列,c#會把回撥註冊到IOCP。這裡延伸一下,在很多系統裡,很多IO網路模型其實是屬於同步範疇的,比如多路複用技術,真正非同步非阻塞的推薦windows下的IOCP。
現在很多現代語言都支援更優秀的回撥方式,比如js和c# 現在都支援async 和await方式來進行非同步操作。
據說windows下的IOCP才是真正的非同步非阻塞模型,求留言區驗證!
非同步的特點
優勢
1.非同步操作無須額外的執行緒負擔,使用回撥的方式進行後續處理,在設計良好的情況下,處理函式可以不必使用共享變數(即使無法完全不用,最起碼可以減少 共享變數的數量),減少了死鎖的可能。
2.執行緒數量的減少,減少了執行緒上下文在cpu切換的開銷。
3.微服務環境(呼叫多個服務介面的情況下)加快了上層介面的響應時間,意味著增加了上層介面的吞吐量
劣勢
1.非同步操作傳統的做法都是透過回撥函式來實現,與同步的思維有些差異,而且難以除錯
2.如果當前環境有操作順序的要求,非同步操作為了保證執行的順序需要做額外的工作
3.由於多數情況下非同步的回撥過程中的執行執行緒並非原來的執行緒,所以在捕獲異常,上下文傳遞等方面需要做特殊處理,特別是不同執行緒共享程式碼或共享資料時容易出問題。
寫在最後
1.在併發量較小的情況下,阻塞式 IO和非同步IO的差距可能不是那麼明顯,但隨著併發量的增加,非同步IO的優勢將會越來越大,吞吐率和效能上的差距也會越來越明顯。
2.在壓力比較小的情況下,一般非同步請求的響應時間大於同步請求的響應時間,因為非同步的回撥也是需要時間的
3.在大併發的情況下,採用非同步呼叫的程式所用執行緒數要遠遠小於同步呼叫程式所用的執行緒數,cpu使用率也一樣(因為避免了太多執行緒上下文切換的成本)
為了系統效能,不要讓任何裝置停下來休息
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69923331/viewspace-2669566/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 問世間「post」為何物?
- 程式設計師的“非程式設計師”之路程式設計師
- 程式設計師的禪修之路程式設計師
- 程式設計師修神之路--分散式系統設計理念這麼難學?程式設計師分散式
- 程式設計師修神之路—高併發優雅的做限流(有福利)程式設計師
- 程式設計師修煉之路 - 設計能力提升途徑程式設計師
- 程式設計師修神之路--簡約而不簡單的分散式通訊基石程式設計師分散式
- 讓程式設計師崩潰的瞬間(非程式設計師勿入)程式設計師
- 【進階之路】併發程式設計(三)-非阻塞同步機制程式設計
- 程式設計師成長祕籍:個人程式設計能力的修煉之路程式設計師
- 程式設計師的成長秘籍:個人程式設計能力的修煉之路程式設計師
- Java程式設計師修煉之路(一)我們為什麼選擇JavaJava程式設計師
- 程式設計師修煉之為什麼程式設計師被稱為“夜貓子”程式設計師
- Java程式設計師從“0到1”的修煉之路Java程式設計師
- 優秀ASP.NET程式設計師修煉之路(收藏)ASP.NET程式設計師
- 一位非科班阿里程式設計師的 CTO 之路阿里程式設計師
- JAVA程式設計師之路Java程式設計師
- 程式設計師修神之路--做好分庫分表其實很難之一(繼續送書)程式設計師
- 提問的智慧 程式設計師成長之路程式設計師
- 神回覆:為什麼程式設計師怕改需求?程式設計師
- 程式設計師修煉之道—程式設計師如何提高自我修養(2)程式設計師
- 程式設計師修煉之道——程式設計師如何提高自我修養(1)程式設計師
- 程式設計師OR非程式設計師,有些程式設計的事需要知道程式設計師
- 我的程式設計師之路程式設計師
- 程式設計師修煉之道程式設計師
- 程式設計師與非程式設計師的思維差異程式設計師
- 美女程式設計師觀點:程式設計師最重要的非程式設計技巧程式設計師
- 超級程式設計師神話程式設計師
- 程式設計師何苦為難程式設計師?程式設計師
- 程式設計師何必難為程式設計師程式設計師
- Java程式設計師成長之路(為程式設計師量身定製的12個目標)【轉】Java程式設計師
- python 網路程式設計----非阻塞或非同步程式設計Python程式設計非同步
- [譯] 非同步程式設計:阻塞與非阻塞非同步程式設計
- 程式設計師的自我修養之全棧程式設計師程式設計師全棧
- 程式設計師的高產之路程式設計師
- 程式設計師的進階之路程式設計師
- JAVA程式設計師成長之路Java程式設計師
- 我的程式設計師之路(六)程式設計師