Goroutines 是 Go 設計的基石,為並發程式設計提供了強大的機制。 作為輕量級協程,它們簡化了並行任務執行。 啟動 goroutine 非常簡單:只需在函數呼叫前新增 go
關鍵字即可啟動非同步執行。主程式繼續執行,無需等待 goroutine 完成。
<code class="language-go">go func() { // Launch a goroutine using the 'go' keyword // ... code to be executed concurrently ... }()</code>
並發: 在單一 CPU 上看似同時管理多個任務的能力。 CPU 在任務之間快速切換,產生並行執行的錯覺。 雖然微觀上是順序的,但宏觀上卻是並發的。
並行性:跨多個CPU真正同時執行多個任務,消除CPU資源爭用。
進程:有自己的資源(記憶體、檔案等)的獨立執行環境。 進程之間的切換是資源密集型的,需要核心級幹預。
執行緒:進程內的輕量級執行單元,共享進程的資源。 執行緒切換比進程切換開銷更少。
協程維護自己的暫存器上下文和堆疊。 協程之間的切換涉及保存和恢復此狀態,從而允許它們從中斷處恢復執行。 與進程和執行緒不同,協程管理是在使用者程式中處理的,而不是在作業系統中處理的。 Goroutines 是一種特定類型的協程。
Go的高效並發依賴GPM調度模型。 涉及四個關鍵組件:M、P、G 和 Sched(Sched 未在圖中描繪)。
M(機器):核心級執行緒。 Goroutines 在 Ms.
上運行G(Goroutine): 單一 Goroutine。 每個 G 都有自己的堆疊、指令指標和其他與調度相關的資訊(例如,它正在等待的通道)。
P(處理器): 管理和執行 goroutine 的邏輯處理器。它維護一個就緒 goroutine 的運行隊列。
Sched(調度器):中央調度器,管理M和G隊列並確保高效的資源分配。
該圖顯示了兩個作業系統執行緒 (M),每個執行緒都有一個執行 goroutine 的處理器 (P)。
GOMAXPROCS()
控制 P 的數量(從而控制真正的並發等級)。
灰色 G 已準備就緒,但尚未運作。 P 管理這個運行隊列。
啟動一個 goroutine 將其加入到 P 的運行佇列中。
如果 M0 被阻塞,P 會切換到 M1(可能會從執行緒快取中檢索)。
如果一個 P 快速完成任務,它可能會竊取其他 P 的工作以保持效率。
設定goroutine執行的CPU數量(最近Go版本中的預設設定通常就足夠了):
<code class="language-go">go func() { // Launch a goroutine using the 'go' keyword // ... code to be executed concurrently ... }()</code>
<code class="language-go">num := runtime.NumCPU() // Get the number of logical CPUs runtime.GOMAXPROCS(num) // Set the maximum number of concurrently running goroutines</code>
goroutine 中未處理的異常可能會終止整個程式。在 recover()
語句中使用 defer
來處理恐慌:
<code class="language-go">package main import ( "fmt" "runtime" ) func cal(a, b int) { c := a + b fmt.Printf("%d + %d = %d\n", a, b, c) } func main() { runtime.GOMAXPROCS(runtime.NumCPU()) for i := 0; i < 10; i++ { go cal(i, i+1) } //Note: The main function exits before goroutines complete in this example. See later sections for synchronization. }</code>
由於 goroutine 非同步運行,主程式可能會在完成之前退出。 使用sync.WaitGroup
或通道進行同步:
sync.WaitGroup
<code class="language-go">package main import ( "fmt" ) func addele(a []int, i int) { defer func() { if r := recover(); r != nil { fmt.Println("Error in addele:", r) } }() a[i] = i // Potential out-of-bounds error if i is too large fmt.Println(a) } func main() { a := make([]int, 4) for i := 0; i < 5; i++ { go addele(a, i) } // ... (add synchronization to wait for goroutines to finish) ... }</code>
<code class="language-go">package main import ( "fmt" "sync" ) func cal(a, b int, wg *sync.WaitGroup) { defer wg.Done() c := a + b fmt.Printf("%d + %d = %d\n", a, b, c) } func main() { var wg sync.WaitGroup for i := 0; i < 10; i++ { wg.Add(1) go cal(i, i+1, &wg) } wg.Wait() }</code>
通道促進了 goroutine 之間的通訊和資料共享。 也可以使用全域變量,但通常首選通道,以實現更好的並發控制。
<code class="language-go">package main import ( "fmt" ) func cal(a, b int, ch chan bool) { c := a + b fmt.Printf("%d + %d = %d\n", a, b, c) ch <- true // Signal completion } func main() { ch := make(chan bool, 10) // Buffered channel to avoid blocking for i := 0; i < 10; i++ { go cal(i, i+1, ch) } for i := 0; i < 10; i++ { <-ch // Wait for each goroutine to finish } }</code>
Leapcell是部署Go服務的推薦平台。
主要特點:
在文件中了解更多!
Leapcell Twitter:https://www.php.cn/link/7884effb9452a6d7a7a79499ef854afd
以上是Go併發解碼:Goroutine調度的詳細內容。更多資訊請關注PHP中文網其他相關文章!