goroutine 和 線程的區別


我們在使用Go語言進行開發時,一般會使用goroutine來處理並發任務。那么大家有沒有考慮過goroutine的實現機制是什么樣的?很多同學會把goroutine與線程等同起來,但是實際上並不是這樣的。在這邊文章中,我們將介紹以下內容:
什么是goroutine?
Goroutine與線程的區別
Goroutine是如何調度的

什么是goroutine?
Goroutine是建立在線程之上的輕量級的抽象。它允許我們以非常低的代價在同一個地址空間中並行地執行多個函數或者方法。相比於線程,它的創建和銷毀的代價要小很多,並且它的調度是獨立於線程的。在golang中創建一個goroutine非常簡單,使用“go”關鍵字即可:
package mainimport ( "fmt" "time")func learning() { fmt.Println("My first goroutine")}func main() { go learning() /* we are using time sleep so that the main program does not terminate before the execution of goroutine.*/ time.Sleep(1 * time.Second) fmt.Println("main function")}
這段代碼的輸出是這樣的:
My first goroutinemain function
如果把Sleep去掉的話,輸出就會變成:
main function
這是因為,和線程一樣,golang的主函數(其實也跑在一個goroutine中)並不會等待其它goroutine結束。如果主goroutine結束了,所有其它goroutine都將結束。
Goroutine與線程的區別
許多人認為goroutine比線程運行得更快,這是一個誤解。Goroutine並不會更快,它只是增加了更多的並發性。當一個goroutine被阻塞(比如等待IO),golang的scheduler會調度其它可以執行的goroutine運行。與線程相比,它有以下幾個優點:
內存消耗更少:
Goroutine所需要的內存通常只有2kb,而線程則需要1Mb(500倍)。
創建與銷毀的開銷更小
由於線程創建時需要向操作系統申請資源,並且在銷毀時將資源歸還,因此它的創建和銷毀的開銷比較大。相比之下,goroutine的創建和銷毀是由go語言在運行時自己管理的,因此開銷更低。

切換開銷更小
這是goroutine於線程的主要區別,也是golang能夠實現高並發的主要原因。線程的調度方式是搶占式的,如果一個線程的執行時間超過了分配給它的時間片,就會被其它可執行的線程搶占。在線程切換的過程中需要保存/恢復所有的寄存器信息,比如16個通用寄存器,PC(Program Counter),SP(Stack Pointer),段寄存器等等。
而goroutine的調度是協同式的,它不會直接地與操作系統內核打交道。當goroutine進行切換的時候,之后很少量的寄存器需要保存和恢復(PC和SP)。因此gouroutine的切換效率更高。
Goroutine的調度
真如前面提到的,goroutine的調度方式是協同式的。在協同式調度中,沒有時間片的概念。為了並行執行goroutine,調度器會在以下幾個時間點對其進行切換:
Channel接受或者發送會造成阻塞的消息
當一個新的goroutine被創建時
可以造成阻塞的系統調用,如文件和網絡操作
垃圾回收
下面讓我們來看一下調度器具體是如何工作的。Golang調度器中有三個概念
Processor(P)
OSThread(M)
Goroutines(G)
在一個Go程序中,可用的線程數是通過GOMAXPROCS來設置的,默認值是可用的CPU核數。我們可以用runtime包動態改變這個值。OSThread調度在processor上,goroutines調度在OSThreads上,如下圖所示

Golang的調度器可以利用多processor資源,在任意時刻,M個goroutine需要被調度到N個OS threads上,同時這些threads運行在至多GOMAXPROCS個processor上(N <= GOMAXPROCS)。Go scheduler將可運行的goroutines分配到多個運行在一個或多個processor上的OS threads上。
每個processor有一個本地goroutine隊列。同時有一個全局的goroutine隊列。每個OSThread都會被分配給一個processor。最多只能有GOMAXPROCS個processor,每個processor同時只能執行一個OSThread。Scheculer可以根據需要創建OSThread。

在每一輪調度中,scheduler找到一個可以運行的goroutine並執行直到其被阻塞。
Search in the local queueif not found Try to steal from other Ps' local queue //see Fig 3 if not found Search in the global queue Also periodically it searches in the global queue (every ~ 1/70)
由此可見,操作系統的一個線程下可以並發執行上千個goroutine,每個goroutine所占用的資源和切換開銷都很小,因此,goroutine是golang適合高並發場景的重要原因

轉載於https://baijiahao.baidu.com/s?id=1620972759226100794&wfr=spider&for=pc,請點進詳看


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM