有關 協程 原理, 見 《協程 和 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 語法糖 上 糾纏下去 像是一個 泥塘 , 新一代 的 架構 是 從新 整理 線程 這個 東西 , 在 語言 級別 實現 語言級 的 「線程」(協程) 是一個 最好 的 平衡點 。