系統線程和goroutine的區別

從棧空間上,goroutine的棧空間更加動態靈活。

每個OS的線程都有一個固定大小的棧內存,通常是2MB,棧內存用於保存線程執行期間的局部變量,且大小是固定不變的,在多變的場景下,這樣固定大小的棧,既太大,又太小,往往不能滿足多變的場景。
2MB固定大小的棧,對於執行簡單操作的goroutine來說,是一種巨大的浪費;但對於執行高度複雜的goroutine來說,又太過於小了。爲了適應不同場景,goroutine在生命週期開始時只有一個很小的棧,典型情況是2KB, 在go程序中,一次創建十萬左右的goroutine也不罕見(2KB*100,000=200MB)。而且goroutine的棧不是固定大小,它可以按需增大和縮小,最大限制可以到1GB。

從調度上看,goroutine的調度開銷遠遠小於線程調度開銷。

OS的線程由OS內核調度,每隔幾毫秒,硬件就發送中斷到CPU,CPU調用調度器內核函數,進而引發當前線程的暫停和下個線程的運行,而線程與線程之間的切換,需要一個完整的上下文切換。由於引發內存訪問數量的增加和CPU等待週期的增加,這一操作是非常耗時的。
但是Go運行的時候包含一個自己的調度器,這個調度器使用一個稱爲一個M:N調度技術,把m個goroutine調度到n個os線程上運行,我們可以用GOMAXPROCS來控制n的數量,Go的調度器不是由硬件時鐘來定期觸發的,而是由特定的go語言結構來觸發的,全部在用戶態實現,不需要切換到內核語境,因此調度一個goroutine比調度一個線程的成本低很多。

goroutine沒有標識

在大部分支持多線程的操作系統和編程語言中,線程有一個獨特的標識,通常是一個整數或者指針,這個特性可以讓我們構建一個線程的局部存儲,本質是一個全局的map,以線程的標識作爲鍵,這樣每個線程可以獨立使用這個map存儲和獲取值,不受其他線程干擾。
goroutine中沒有可供程序員訪問的標識,原因是一種純函數的理念,不希望濫用線程局部存儲導致一個不健康的超距作用,即函數的行爲不僅取決於它的參數,還要取決於運行它的線程標識,這就造成了函數的行爲變得詭異莫測,與Go語言所鼓勵的簡單的編程風格不匹配。

參考:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章