golang-concurrency

golang-concurrency

熱門

Golang 並發模式。在撰寫或審查涉及 goroutine、channel、select、鎖、sync 原語、errgroup、singleflight、worker pool 或 fan-out/fan-in pipeline 的並發 Go 程式碼時使用。當你偵測到 goroutine 洩漏、競爭條件、channel 所有權問題,或需要在 channel 與 mutex 之間做選擇時也會觸發。

2261星標
150分支
更新於 2026/6/6
SKILL.md
readonlyread-only
name
golang-concurrency
description

Golang 並發模式。在撰寫或審查涉及 goroutine、channel、select、鎖、sync 原語、errgroup、singleflight、worker pool 或 fan-out/fan-in pipeline 的並發 Go 程式碼時使用。當你偵測到 goroutine 洩漏、競爭條件、channel 所有權問題,或需要在 channel 與 mutex 之間做選擇時也會觸發。

角色設定: 你是一位 Go 並發工程師。你假設每個 goroutine 都是一項負債,直到證明其必要性為止——正確性與無洩漏優先於效能。

模式:

  • 撰寫模式 — 實作並發程式碼(goroutine、channel、sync 原語、worker pool、pipeline)。請依序遵循以下指示。
  • 審查模式 — 審查 PR 中的並發程式碼變更。專注於 diff:檢查 goroutine 洩漏、缺少 context 傳遞、所有權違規以及未保護的共享狀態。依序進行。
  • 稽核模式 — 稽核整個程式碼庫中現有的並發程式碼。如「並行化並發稽核」一節所述,最多使用 5 個平行子代理。

社群預設。 明確取代 samber/cc-skills-golang@golang-concurrency 技能的團隊技能具有優先權。

Go 並發最佳實務

Go 的並發模型建立在 goroutine 和 channel 之上。Goroutine 雖然輕量,但並非免費——你產生的每個 goroutine 都是必須管理的資源。目標是結構化並發:每個 goroutine 都有明確的擁有者、可預測的結束方式以及正確的錯誤傳遞。

核心原則

  1. 每個 goroutine 都必須有明確的結束方式 — 沒有關閉機制(context、done channel、WaitGroup)會導致洩漏並累積,直到程序崩潰
  2. 透過通訊共享記憶體 — channel 明確傳遞所有權;mutex 保護共享狀態但使所有權隱含
  3. 在 channel 上傳送副本而非指標 — 傳送指標會產生隱藏的共享記憶體,違背 channel 的目的
  4. 只有傳送者能關閉 channel — 從接收端關閉會導致 panic(若傳送者在關閉後寫入)
  5. 指定 channel 方向chan<-<-chan)— 編譯器在編譯時防止誤用
  6. 預設使用無緩衝 channel — 較大的緩衝區會掩蓋背壓;僅在有測量依據時使用
  7. 在 select 中永遠包含 ctx.Done() — 否則 goroutine 會在呼叫者取消後洩漏
  8. 避免在熱迴圈中重複使用 time.After — 每次呼叫都會分配計時器並造成不必要的開銷;對於長時間運行的迴圈,請使用 time.NewTimer + Reset
  9. 在測試中使用 go.uber.org/goleak 追蹤 goroutine 洩漏

如需詳細的 channel/select 程式碼範例,請參閱 Channels and Select Patterns

Channel vs Mutex vs Atomic

情境 使用 原因
在 goroutine 之間傳遞資料 Channel 傳達所有權轉移
協調 goroutine 生命週期 Channel + context 透過 select 進行乾淨關閉
保護共享結構體欄位 sync.Mutex / sync.RWMutex 簡單的臨界區段
簡單計數器、旗標 sync/atomic 無鎖,較低開銷
多讀取者、少寫入者的 map sync.Map 針對讀取密集型工作負載最佳化。並發 map 讀寫會導致程式直接崩潰
快取昂貴的計算結果 sync.Once / singleflight 執行一次或去重

WaitGroup vs errgroup

需求 使用 原因
等待 goroutine,不需要錯誤 sync.WaitGroup 發射後忘記
等待 + 收集第一個錯誤 errgroup.Group 錯誤傳遞
等待 + 在第一個錯誤時取消同儕 errgroup.WithContext 錯誤時取消 context
等待 + 限制並發數 errgroup.SetLimit(n) 內建 worker pool

Sync 原語快速參考

原語 使用案例 關鍵備註
sync.Mutex 保護共享狀態 保持臨界區段簡短;絕不在 I/O 期間持有鎖
sync.RWMutex 多讀取者、少寫入者 絕不從 RLock 升級到 Lock(會死結)
sync/atomic 簡單計數器、旗標 偏好型別化原子操作(Go 1.19+):atomic.Int64atomic.Bool
sync.Map 並發 map,讀取密集型 無需明確鎖定;當寫入為主時使用 RWMutex+map
sync.Pool 重複使用暫用物件 Put() 前務必 Reset();減少 GC 壓力
sync.Once 一次性初始化 Go 1.21+:OnceFuncOnceValueOnceValues
sync.WaitGroup 等待簡單的 goroutine Go 1.25+:對於不會 panic 且不需要錯誤傳遞的發射後等待任務,偏好使用 wg.Go(func(){ ... })。對於 Go <1.25 使用 Add/Done。對於錯誤/取消/限制,使用帶 context 的 errgroup
x/sync/singleflight 去重並發呼叫 防止快取雪崩
x/sync/errgroup Goroutine 群組 + 錯誤 SetLimit(n) 取代手動實作的 worker pool

如需詳細範例和反模式,請參閱 Sync Primitives Deep Dive

並發檢查清單

在產生 goroutine 之前,請回答:

  • [ ] 它如何結束? — context 取消、channel 關閉或明確訊號
  • [ ] 我能通知它停止嗎? — 傳遞 context.Context 或 done channel
  • [ ] 我能等待它嗎?sync.WaitGrouperrgroup
  • [ ] 誰擁有 channel? — 建立者/傳送者擁有並關閉
  • [ ] 這是否應該改為同步? — 不要未經測量就加入並發

Pipeline 與 Worker Pool

關於 pipeline 模式(fan-out/fan-in、有界 worker、生成器鏈、Go 1.23+ iterator、samber/ro),請參閱 Pipelines and Worker Pools

並行化並發稽核

在稽核大型程式碼庫的並發問題時,最多使用 5 個平行子代理(Agent 工具):

  1. 找出所有 goroutine 產生點(go funcgo method)並驗證關閉機制
  2. 搜尋沒有同步機制的可變全域變數和共享狀態
  3. 稽核 channel 使用方式——所有權、方向、關閉、緩衝區大小
  4. 找出迴圈中的 time.After、select 中缺少的 ctx.Done()、無限制的 goroutine 產生
  5. 檢查 mutex 使用、sync.Map、原子操作以及執行緒安全文件

常見錯誤

錯誤 修正
發射後忘記的 goroutine 提供停止機制(context、done channel)
從接收端關閉 channel 只有傳送者能關閉
熱迴圈中的 time.After 重複使用 time.NewTimer + Reset
select 中缺少 ctx.Done() 永遠在 select 中包含 context 以允許取消
無限制的 goroutine 產生 使用 errgroup.SetLimit(n) 或 semaphore
透過 channel 共享指標 傳送副本或不可變值
在 goroutine 內部呼叫 wg.Add go 之前呼叫 Add——否則 Wait 可能提前返回
在 CI 中忘記 -race 永遠執行 go test -race ./...
在 I/O 期間持有 mutex 保持臨界區段簡短

交叉參考

  • -> 參閱 samber/cc-skills-golang@golang-performance 技能以了解 false sharing、cache-line padding、sync.Pool 熱路徑模式
  • -> 參閱 samber/cc-skills-golang@golang-context 技能以了解取消傳遞和逾時模式
  • -> 參閱 samber/cc-skills-golang@golang-safety 技能以了解並發 map 存取和競爭條件預防
  • -> 參閱 samber/cc-skills-golang@golang-troubleshooting 技能以了解除錯 goroutine 洩漏和死結
  • -> 參閱 samber/cc-skills-golang@golang-design-patterns 技能以了解優雅關閉模式
  • -> 參閱 samber/cc-skills-golang@golang-continuous-integration 技能以了解在 CI 中使用這些指南進行自動化 AI 驅動的程式碼審查

Go 1.26 實驗性 goroutine 洩漏分析

對於 Go 1.26 診斷,有一個實驗性的 goroutine 洩漏分析功能。它對於生產環境的洩漏調查很有用,但需要透過 GOEXPERIMENT=goroutineleakprofile 啟用;不要將其視為預設的穩定行為。

啟用實驗功能時的典型用法:

curl http://localhost:6060/debug/pprof/goroutineleak?debug=2
go tool pprof http://localhost:6060/debug/pprof/goroutineleak

保留現有工具:

  • 測試:go.uber.org/goleak
  • 執行時期計數:runtime.NumGoroutine()
  • 堆疊傾印:/debug/pprof/goroutine?debug=2
  • 競爭檢查:go test -race ./...

參考資料