再見 異步回調, 再見 Async Await, 10 萬 個 協程 的 時代 來 了

有關 協程 原理,  見 《協程 和 async await》   http://www.javashuo.com/article/p-xmwneoss-ec.html  ,html

 

協程 切換 的時間很快,   就是 保存 3 個 寄存器 的 值, 再 修改 3 個 寄存器 的 值,程序員

這  3 個 寄存器 分別 保存的 是  協程 的 棧頂 棧底 指令計數器,服務器

 

切換 協程 就是  保存 上一個 協程 的  棧頂 棧底 指令計數器 ,   再 把 寄存器 的 值 修改 爲 下一個 協程 的 棧頂 棧底 指令計數器  。網絡

 

因此,  協程 切換 至關於 是 調用一個 很短 的  方法,  時間複雜度 能夠認爲是  O(1)    。閉包

 

我在  《協程 和 async await》   中 提到, 協程 避免 閉包 共享變量 帶來的 二次尋址 的 性能消耗,架構

 

可是,實際上,    有了 協程  的 話,    已經不須要 異步回調 來   減小 線程數量 和 線程切換時間,  固然 也不須要  async await  。併發

 

咱們能夠 正常 的 編寫 同步調用 的 方法,好比,  讀取文件, 能夠調用 FileStream 的 Read()   方法,  而 不須要 調用 ReadAsync() 或者 BeginRead() 方法 。異步

 

固然,這個 Read()  方法 須要用  協程 從新實現,   Read()  方法 的 同步等待 要用 協程 的 同步等待 來實現,而不是 線程 的 同步等待 。async

 

協程 的 同步等待 和 線程 同樣,   在 Read() 方法 裏,   當 調用 操做系統 的 異步 IO   API 時,   當前 協程 掛起,  當前 線程 轉而 執行其它 協程 ,高併發

當 異步 IO 完成時,會把  調用這個 IO 的 掛起 的 協程 喚醒(放入 就緒隊列),這樣 接下來 很快就會 執行到 這個 協程  了  。

 

這和 線程 是同樣的  。    但 我就不知道 操做系統 到底 作了些什麼,   以致於 線程 這個 「資源」  如此 「昂貴」,  搞得 這些年 流行 炒做 線程,   像  炒股 同樣 炒 的 很熱,

什麼    異步回調 流, libuv, IOCP , ePoll ,  async await ,  狀態機,  編譯器 黑魔法,   GoRoutine,  CoRoutine     ……

Go  語言 的  介紹    每次都 強調 「支持高併發」, 「編寫服務器端程序」  ,       我原來還奇怪,  高併發 這個不是 很日常 麼,  C# 不也是 高併發 沒毛病  ……

 

這幾天 才 想起來,  原來  Go   強調 「支持 高併發」   大概 是  由於 有  GoRoutine  啊   ~~  !

 

這些 基於 異步回調  和  語法糖   的 作法,   將 代碼  切割 的 支離破碎,    語法糖  篡改了  原始代碼 ,  讓 編譯器 變得 笨重複雜,  讓  程序員 的 代碼 和 編譯器 的 代碼 變得 難以理解 ,      讓 編譯器 技術    升維  爲   大型 軟件工程   。

 

我在 《協程 和 async await》  中 提到,  協程 須要 編譯器 在 堆 裏 模擬 線程 的  棧 和 上下文 保存區,   其實還須要 模擬同樣,  就是 操做系統(CPU) 的   時間片 調度,        這可有點難,  弄很差 性能 就 一落千尺,  與 咱們 輕量 高性能 的 目標 背道而馳  。

 

可是,  實際上,  協程 基本上 不須要 時間片 調度,    只須要 不停執行 就能夠,   只有 遇到 IO 的 時候 才 掛起, 執行下一個 協程,  就能夠了   。

 

這樣就 差很少 了  。

 

在 協程 架構下,  對於 每個 請求 ,  能夠 建立一個 協程 來處理,處理完了 協程 就 銷燬,  能夠 存在 大量  協程  ,

這 體現 出 與 線程 的 區別 是,   不須要 考慮  建立開銷,  不須要 考慮  切換開銷,  不須要 考慮(協程) 數量  。

 

在 一臺 服務器 上,    同時 運行  10 萬 個 協程 也能夠   。

 

這樣就能夠和        異步回調  ,   Async  Await        說 再見 了     。

 

這樣 就能夠  「迴歸最柔軟的初心, 詩意的棲居在大地上」    ,     什麼是   「柔軟的初心」     ?

 

就是  80 年代 的 蘋果電腦 、 中華學習機 、 286 、 Basic 、  90 年代  的  譚浩強 爺爺 的 C 語言   。

還有  70 年代 的 Unix  、  貝爾實驗室  、   網絡鏈路 和 網路協議   。

 

協程   是   ILBC / D#    的  一大 賣點    。

 

你們可能 擔憂  對 協程 的 就緒隊列 和 掛起隊列 的 隊列 讀寫 須要 Lock(同步互斥),  這個 Lock  的 性能 怎麼樣,

我以前在   ILBC / D#     中 提到過   ILBC  用 CAS 指令 實現  IL Lock ,  用於  堆 管理(讀寫 堆  表),

這個作法 和 C# Java 的 new 操做  讀寫堆表 時 用的 作法 應該是同樣的(我推測)  。

CAS  實現 的 lock 是 很快 的,  能夠認爲 是 指令級 的 ,   能夠認爲 時間複雜度 是 O(1)  。

事實上,  測試  C#  的 new 操做 的 性能 大約 是  操做系統 lock (lock 關鍵字 / Monitor.Enter()) 的  10  倍  。

或者說,  C#  的 new 操做 的 時間花費 大約 是  操做系統 lock (lock 關鍵字 / Monitor.Enter()) 的  1/10    。

 

在  「異步回調」  和  async await 語法糖  上  糾纏下去 像是一個 泥塘 ,   新一代 的 架構 是 從新 整理 線程 這個 東西 ,     在 語言 級別 實現 語言級 的 「線程」(協程) 是一個 最好 的 平衡點    。

相關文章
相關標籤/搜索