:information_source:  本文基於Go 1.13

Go中創造所有的goroutine都是由內部的調度器管理。Go調度器會嘗試爲所有goroutine分配運行時間,並在當前goroutine被阻止或終止時使所有CPU忙於運行goroutine。它實際上是作爲特殊的goroutine運行的。

調度協程

Go通過 GOMAXPROCS 變量限制了運行的系統線程數。這意味着Go必須在每個正在運行的線程上調度和管理goroutine。該角色被委派給名爲 g0 的特殊goroutine,這是爲每個系統線程創建的第一個goroutine:

然後,它將安排就緒的goroutine在系統線程上運行。

爲了更好地瞭解在 g0 上的調度方式,讓我們回顧一下通道的使用情況。這是當goroutine阻塞在通道上發送時:

ch := make(chan int)

[...]

ch <- v

在通道上阻塞時,當前goroutine將被停放,即處於等待模式,並且不會在任何goroutine隊列中被推送:

然後, g0 替換goroutine並進行第一輪調度:

在調度期間,本地隊列擁有優先級,並且goroutine#2現在將運行:

一旦接收器將讀取通道,則goroutine#7將被解除阻塞:

v := <-ch

接收到消息的goroutine將切換到 g0 並通過將其放置在本地隊列中來解鎖停放的goroutine:

特殊的goroutine除了管理調度,它還有更多的職責。

職責範圍

與一般goroutine相反, g0 擁有固定的較大的棧。這樣,Go可以在需要更大棧並且在不希望棧增長的情況下執行操作。在 g0 的職責中,我們可以列出:

  • Goroutine創建。當調用 go func(){ ... }()go myFunction() 時,Go會將函數創建委託給 g0 ,然後再將其放置在本地隊列中。新創建的goroutine優先運行,並放置在本地隊列的頂部。

  • Defer方法分配。

  • Gc操作,例如stw,掃描goroutine的棧以及一些標清操作。

  • 棧增長。在需要時,Go會增加goroutine的大小。該操作由 g0 在prolog方法中完成。

這個特殊的goroutine g0 涉及許多其他操作(大量分配,cgo等),使我們的程序可以更高效地管理操作,並且需要更大的棧,以保持我們的程序在低內存下更加高效。

編譯整理自  Go: g0, Special Goroutine

https://medium.com/a-journey-with-go/go-g0-special-goroutine-8c778c6704d8

相關文章