協同式多工解釋了 Coroutine(協程)為什麼能在排程上比執行緒(Thread)便宜,但還沒回答另一個問題:當一個 Coroutine 被暫停又恢復時,它的執行狀態到底存在哪裡?這就牽涉到 Coroutine 的另一個核心分類:有堆疊協程(Stackful Coroutine)與無堆疊協程(Stackless Coroutine)。
為什麼需要保存狀態#
不論是執行緒還是 Coroutine,「暫停 / 恢復」的本質都是:
- 把當前的執行位置記下來(程式計數器、變數、呼叫關係)。
- 等下次被排程時,從記下的位置繼續往下跑。
執行緒是怎麼做到的?每條執行緒都有自己的呼叫堆疊(Call Stack),切換時 OS 會儲存暫存器、堆疊指標、頁表狀態等資訊,下次再還原。這套機制完整但成本高。
Coroutine 也要解決同樣的問題,不過它有兩種設計選擇。
有堆疊協程(Stackful Coroutine)#
有堆疊協程給每個 Coroutine 配一個獨立的呼叫堆疊:
- 暫停時把整個堆疊儲存起來,恢復時換回來。
- 因為有自己的堆疊,所以可以在任意函式裡呼叫
yield()或對應的暫停 API,不限制呼叫位置。 - 缺點是每個 Coroutine 都要付出堆疊空間的成本,數量大時記憶體占用會明顯。
許多語言用的是這條路線,例如 Lua 的 coroutine、Go 的 goroutine(雖然 Go runtime 對堆疊做了動態調整)。
無堆疊協程(Stackless Coroutine)#
Kotlin 的 Coroutine 走的是另一條路:無堆疊協程。它不為每個 Coroutine 配一個獨立堆疊,而是把暫停所需的狀態存在物件裡。
關鍵字是 Continuation:
- 每個 suspend 函式(suspend function)背後都隱含一個
Continuation實例。 - Coroutine 暫停時,把當下的區域變數、執行位置等資訊塞進
Continuation。 - 恢復時呼叫
Continuation的resumeWith(...),從上次斷點繼續。
換句話說,Continuation 就是 Coroutine 內部的回呼(Callback),編譯器把 suspend 函式拆成一個狀態機,每個暫停點就是一個狀態。從外面看是線性的程式,從內部看是回呼鏈。
Kotlin Coroutine 使用 CPS(Continuation-Passing Style)轉換實作 suspend。雖然底層仍是回呼,但這層轉換由編譯器自動完成,使用者寫的還是線性風格的程式。
為什麼 Kotlin 選擇無堆疊#
選擇無堆疊有幾個直接好處:
- 記憶體輕:不需要為每個 Coroutine 保留獨立堆疊,狀態只是個物件。
- 可以與 JVM 緊密整合:Coroutine 跑在普通的 JVM 執行緒上,對偵錯與監控工具友善。
- 對函式庫作者友善:不必額外維護堆疊管理。
代價也很明顯:
- 不能在任意位置
suspend:suspend 函式只能從 suspend 函式或 Coroutine 範圍內呼叫,不能在普通函式中間隨手讓出。 - 編譯後狀態機變多,呼叫鏈在堆疊追蹤上會比較不直覺。
CoroutineScope 與 Continuation 的關係#
第一個 Coroutine 程式裡用到的 coroutineScope { ... },作用之一就是提供承載 Continuation 的環境。當你在 scope 中呼叫 suspend 函式時:
- 編譯器會把
Continuation傳進去。 - suspend 函式利用這個
Continuation把工作完成後的「下一步」串起來。 - 因為
Continuation與 scope 綁在一起,scope 結束時內部的 Coroutine 也會收尾。
這也解釋了 suspend 函式為什麼一定要在 CoroutineScope 中執行:沒有 scope 就沒有對應的 Continuation,編譯器無從產生狀態機的串接。
小結#
- Coroutine 的「暫停 / 恢復」要解決的是執行狀態保存的問題。
- 有堆疊協程(Stackful Coroutine)給每個 Coroutine 一個獨立堆疊,靈活但成本高。
- 無堆疊協程(Stackless Coroutine)把狀態放進
Continuation物件,輕量但需要suspend配合。 - Kotlin Coroutine 選擇了無堆疊路線,搭配編譯器的 CPS 轉換,讓我們可以用線性語法寫非同步程式。
原文出處#
- 原書/iThome:https://ithelp.ithome.com.tw/articles/10261501
- 原書/iThome:https://ithelp.ithome.com.tw/articles/10262296
- 書本:Kotlin 小宇宙(iThome 鐵人賽系列書)