在Go語言中,GMP調度模型是實現并發的重要手段之一。GMP調度模型的核心思想是將M(Machine)、G(Goroutine)和P(Processor)三個概念分離開來,通過調度器來協調它們之間的關系,從而實現高效的并發。
M(Machine)
M代表著操作系統中的線程,它是Go語言中的執行單位。在程序啟動時,Go語言會創建一定數量的M,每個M都會綁定一個P。M的數量默認是CPU核心數,但是可以通過GOMAXPROCS環境變量來設置。
G(Goroutine)
Goroutine是Go語言中的輕量級線程,它可以與M一起調度執行。在程序中,我們可以通過關鍵字go來啟動一個Goroutine,例如:
go func() {// 處理業務邏輯}()
在上面的例子中,我們使用Go關鍵字啟動了一個Goroutine,并在其中執行業務邏輯。需要注意的是,Goroutine是由Go語言的運行時(runtime)進行調度的,而不是由操作系統進行調度,因此它具有輕量級、高效等特點。
P(Processor)
Processor是Go語言中的處理器,它負責將Goroutine分配給M執行。每個M都會綁定一個P,而P的數量可以通過runtime.NumCPU()來獲取(不同于M的數量)。
調度器
調度器是GMP調度模型的核心,它負責將Goroutine分配給M執行,并在M的數量不足時創建新的M。調度器還可以將M從一個P轉移到另一個P,以達到負載均衡的目的。
調度器的實現方式比較復雜,但是它的工作原理可以簡單概括如下:
– 當一個Goroutine被啟動時,它會被放入一個全局的運行隊列中(稱為全局隊列)。
– 當一個M空閑時,它會從全局隊列中獲取一個Goroutine,并開始執行它。
– 當一個Goroutine阻塞時,它會被放入一個本地的等待隊列中(稱為本地隊列)。
– 當一個M中的本地隊列為空時,它會從全局隊列中獲取一批Goroutine,并將它們放入本地隊列中。
– 當一個P中的本地隊列為空時,它會從其他P中的本地隊列中獲取一批Goroutine,并將它們放入本地隊列中。
– 當一個M執行時間過長時,調度器會中斷它的執行,并將它的狀態保存到一個全局的掛起隊列中。下次該M被分配到執行時,它會從掛起隊列中恢復狀態,并繼續執行。
– 當一個M執行的Goroutine數量達到一定閾值時,調度器會將它的狀態保存到一個全局的休眠隊列中。下次該M被分配到執行時,它會從休眠隊列中恢復狀態,并繼續執行。
下面我們來看一個簡單的示例,它通過啟動多個Goroutine來計算斐波那契數列的值:
package mainimport "fmt"func main() {for i := 0; i < 10; i {go func() {fmt.Println(fib(40))}()}}func fib(n int) int {if n < 2 {return n}return fib(n-1) fib(n-2)}
在上面的例子中,我們啟動了10個Goroutine,并在其中計算斐波那契數列的值。由于斐波那契數列的計算是CPU密集型的,因此這個程序會利用GMP調度模型來實現高效的并發。
注意事項
在使用GMP調度模型時,需要注意以下幾點:
– 不要在Goroutine中阻塞或者進行長時間的計算,這會導致M被掛起或者休眠,從而影響程序的性能。
– 不要在Goroutine中訪問共享資源時不加鎖,這會導致數據競爭,從而引發難以排查的bug。
– 不要將過多的Goroutine放入全局隊列中,這會導致調度器的性能下降,從而影響程序的性能。
– 不要將過多的M創建出來,這會導致系統資源的浪費,從而影響程序的性能。
#從今天起記錄我的2023#
版權聲明:本文內容由互聯網用戶自發貢獻,該文觀點僅代表作者本人。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如發現本站有涉嫌抄襲侵權/違法違規的內容, 請發送郵件至 舉報,一經查實,本站將立刻刪除。