位元組一面:go的協程相比執行緒,輕量在哪?
- 2022 年 3 月 15 日
- 筆記
- Cloud Native
1. 用戶態和內核態
Linux整個體系分為用戶態和內核態(或者叫用戶空間和內核空間), 那內核態究竟是什麼呢?
本質上我們所說的內核態, 它是一種特殊的軟體程式,特殊在哪?
統籌電腦的硬體資源,例如協調CPU資源、分配記憶體資源、並且提供穩定的環境供應用程式運行。
2. 為什麼執行緒切換會導致用戶態和內核態的切換?
- 執行緒是cpu調度的基本單位,進程是資源佔有的基本單位。
- 因為執行緒中的程式碼是在用戶態運行,而執行緒的調度是在內核態,所以執行緒切換會觸發用戶態和內核態的切換。
- 執行緒上下文切換的代價是高昂的:上下文切換的延遲取決於不同的因素,大概是50到100 ns左右,考慮到硬體平均在每個核心上每ns執行12條指令,那麼一次上下文切換可能會花費600到1200條指令的延遲時間。
3. 導致執行緒上下文切換的時機
| <1>. 自發性上下文切換 | 自發性上下文切換指執行緒由於自身因素導致的切出 |
|---|---|
| Thread.sleep() | 執行緒主動休眠 |
| object.wait() | 執行緒等待鎖 |
| Thread.yield() | 當前執行緒主動讓出CPU,如果有其他就緒執行緒就執行其他執行緒,如果沒有則繼續當前執行緒 |
| oThread.join() | 阻塞發起調用的執行緒,直到oThread執行完畢 |
| <2>. 非自發性上下文切換:執行緒由於執行緒調度器的原因被迫切出 |
|---|
| 執行緒的時間片用完 |
| 高優先順序執行緒搶佔 |
| 虛擬機的垃圾回收動作 |
4. 執行緒切換的開銷
| <1>. 直接開銷 |
|---|
| 保存/恢復上下文所需的開銷 |
| 執行緒調度器調度執行緒的開銷 |
| <2>. 間接開銷 |
|---|
| 重新載入高速快取 |
| 上下文切換可能導致 一級快取被沖刷,寫入下一級快取或記憶體 |
5. go的協程輕量級體現在哪?
如上面所述,執行緒切換會導致 用戶態和內核態的切換,其中內核態耗時較長,且不受用戶程式碼控制。
go將goroutine的調度維持在用戶態, 這是由GPM中的P Process來完成的,做用戶態任務的調度器,功能類比於常規的作業系統執行緒調度器,所以又被稱為邏輯處理器。
P 是G、M之間的橋樑,調度器對於goroutine的調度,很明顯也會有切換,這個切換是很輕量的: 只涉及PC SP DX三個暫存器的值的修改;而對比執行緒的上下文切換則需要陷入內核模式、以及16個暫存器的刷新。
6. GO GMP 調度方式
- 由邏輯處理器P調度協程G進系統執行緒M (若本地隊列沒有G,從其他隊列/全局隊列偷取G),
- 執行緒M執行G, 遇到[系統調用], G和M分離,拿新的M去接管原邏輯處理器P
請仔細閱讀上圖,出處不可考證,感謝原圖作者。
這裡特意指出網路IO操作不會走上圖的模型,否則要分配的系統執行緒M依然很多,程式很快就爆滿了。這時G會和邏輯處理器P分離,並移動到netpoller,一旦網路輪詢器通知網路讀/寫就緒,對應G就會重新分配到邏輯器處理器上來完成操作, 在此期間原系統執行緒M可以去做別的G。
ref
- //blog.csdn.net/No_Game_No_Life_/article/details/106100813
- //zhuanlan.zhihu.com/p/261807834
- //chende.ren/2020/12/27221039-008-gmp-model.html
- //studygolang.com/articles/12328
- //www.jianshu.com/p/cc3c0fefee43

