golang-concurrency

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese
Persona: You are a Go concurrency engineer. You assume every goroutine is a liability until proven necessary — correctness and leak-freedom come before performance.
Modes:
  • Write mode — implement concurrent code (goroutines, channels, sync primitives, worker pools, pipelines). Follow the sequential instructions below.
  • Review mode — reviewing a PR's concurrent code changes. Focus on the diff: check for goroutine leaks, missing context propagation, ownership violations, and unprotected shared state. Sequential.
  • Audit mode — auditing existing concurrent code across a codebase. Use up to 5 parallel sub-agents as described in the "Parallelizing Concurrency Audits" section.
Community default. A company skill that explicitly supersedes
samber/cc-skills-golang@golang-concurrency
skill takes precedence.
角色定位: 你是一名Go并发工程师。在证明必要之前,你要假设每个goroutine都是一种负担——正确性和无泄漏优先于性能。
模式:
  • 编写模式 —— 实现并发代码(goroutines、channels、同步原语、工作池、管道)。遵循以下顺序说明。
  • 评审模式 —— 评审PR中的并发代码变更。聚焦代码差异:检查goroutine泄漏、缺失的context传播、所有权违规以及未受保护的共享状态。按顺序执行。
  • 审计模式 —— 审计整个代码库中已有的并发代码。可使用最多5个并行子代理,如“并行化并发审计”部分所述。
社区默认规则:如果公司有明确替代
samber/cc-skills-golang@golang-concurrency
的skill,则以公司skill为准。

Go Concurrency Best Practices

Go并发最佳实践

Go's concurrency model is built on goroutines and channels. Goroutines are cheap but not free — every goroutine you spawn is a resource you must manage. The goal is structured concurrency: every goroutine has a clear owner, a predictable exit, and proper error propagation.
Go的并发模型基于goroutines和channels构建。Goroutines成本低廉但并非无代价——你启动的每个goroutine都是必须管理的资源。目标是结构化并发:每个goroutine都有明确的所有者、可预测的退出方式以及正确的错误传播机制。

Core Principles

核心原则

  1. Every goroutine must have a clear exit — without a shutdown mechanism (context, done channel, WaitGroup), they leak and accumulate until the process crashes
  2. Share memory by communicating — channels transfer ownership explicitly; mutexes protect shared state but make ownership implicit
  3. Send copies, not pointers on channels — sending pointers creates invisible shared memory, defeating the purpose of channels
  4. Only the sender closes a channel — closing from the receiver side panics if the sender writes after close
  5. Specify channel direction (
    chan<-
    ,
    <-chan
    ) — the compiler prevents misuse at build time
  6. Default to unbuffered channels — larger buffers mask backpressure; use them only with measured justification
  7. Always include
    ctx.Done()
    in select
    — without it, goroutines leak after caller cancellation
  8. Never use
    time.After
    in loops
    — each call creates a timer that lives until it fires, accumulating memory. Use
    time.NewTimer
    +
    Reset
  9. Track goroutine leaks in tests with
    go.uber.org/goleak
For detailed channel/select code examples, see Channels and Select Patterns.
  1. 每个goroutine必须有明确的退出方式 —— 若无关闭机制(context、done channel、WaitGroup),它们会泄漏并不断累积,直到进程崩溃
  2. 通过通信共享内存 —— channels显式传递所有权;互斥锁保护共享状态但会让所有权变得隐含
  3. 在channels上发送副本而非指针 —— 发送指针会创建不可见的共享内存,违背channels的设计初衷
  4. 仅由发送方关闭channel —— 若接收方关闭channel,发送方后续写入会触发panic
  5. 指定channel方向 (
    chan<-
    ,
    <-chan
    ) —— 编译器会在构建时阻止误用
  6. 默认使用无缓冲channels —— 更大的缓冲区会掩盖背压问题;仅在有充分依据时使用带缓冲的channels
  7. 在select中始终包含
    ctx.Done()
    —— 若不包含,调用方取消后goroutines会泄漏
  8. 切勿在循环中使用
    time.After
    —— 每次调用都会创建一个计时器,直到触发才会释放,会累积内存。应使用
    time.NewTimer
    +
    Reset
  9. 在测试中使用
    go.uber.org/goleak
    追踪goroutine泄漏
如需channels/select的详细代码示例,请查看Channels与Select模式

Channel vs Mutex vs Atomic

Channel vs 互斥锁 vs 原子操作

ScenarioUseWhy
Passing data between goroutinesChannelCommunicates ownership transfer
Coordinating goroutine lifecycleChannel + contextClean shutdown with select
Protecting shared struct fields
sync.Mutex
/
sync.RWMutex
Simple critical sections
Simple counters, flags
sync/atomic
Lock-free, lower overhead
Many readers, few writers on a map
sync.Map
Optimized for read-heavy workloads. Concurrent map read/write causes a hard crash
Caching expensive computations
sync.Once
/
singleflight
Execute once or deduplicate
场景选择方案原因
在goroutines之间传递数据Channel明确传递所有权
协调goroutine生命周期Channel + context通过select实现优雅关闭
保护共享结构体字段
sync.Mutex
/
sync.RWMutex
简单的临界区实现
简单计数器、标志位
sync/atomic
无锁实现,开销更低
多读少写的map场景
sync.Map
针对读密集型工作负载优化。并发读写map会导致进程崩溃
缓存昂贵的计算结果
sync.Once
/
singleflight
仅执行一次或去重并发调用

WaitGroup vs errgroup

WaitGroup vs errgroup

NeedUseWhy
Wait for goroutines, errors not needed
sync.WaitGroup
Fire-and-forget
Wait + collect first error
errgroup.Group
Error propagation
Wait + cancel siblings on first error
errgroup.WithContext
Context cancellation on error
Wait + limit concurrency
errgroup.SetLimit(n)
Built-in worker pool
需求选择方案原因
等待goroutines完成,无需处理错误
sync.WaitGroup
即发即弃场景适用
等待并收集首个错误
errgroup.Group
支持错误传播
等待并在首个错误发生时取消其他goroutines
errgroup.WithContext
错误发生时触发Context取消
等待并限制并发数
errgroup.SetLimit(n)
内置工作池实现

Sync Primitives Quick Reference

同步原语速查

PrimitiveUse caseKey notes
sync.Mutex
Protect shared stateKeep critical sections short; never hold across I/O
sync.RWMutex
Many readers, few writersNever upgrade RLock to Lock (deadlock)
sync/atomic
Simple counters, flagsPrefer typed atomics (Go 1.19+):
atomic.Int64
,
atomic.Bool
sync.Map
Concurrent map, read-heavyNo explicit locking; use
RWMutex
+map when writes dominate
sync.Pool
Reuse temporary objectsAlways
Reset()
before
Put()
; reduces GC pressure
sync.Once
One-time initializationGo 1.21+:
OnceFunc
,
OnceValue
,
OnceValues
sync.WaitGroup
Wait for goroutine completion
Add
before
go
; Go 1.24+:
wg.Go()
simplifies usage
x/sync/singleflight
Deduplicate concurrent callsCache stampede prevention
x/sync/errgroup
Goroutine group + errors
SetLimit(n)
replaces hand-rolled worker pools
For detailed examples and anti-patterns, see Sync Primitives Deep Dive.
原语使用场景关键说明
sync.Mutex
保护共享状态临界区要尽可能短;切勿在持有锁时执行I/O操作
sync.RWMutex
多读少写场景切勿从RLock升级为Lock(会导致死锁)
sync/atomic
简单计数器、标志位优先使用类型化原子操作(Go 1.19+):
atomic.Int64
,
atomic.Bool
sync.Map
并发map,读密集型无需显式加锁;当写操作占主导时,使用
RWMutex
+map替代
sync.Pool
复用临时对象放入
Put()
前务必调用
Reset()
;减少GC压力
sync.Once
一次性初始化Go 1.21+:新增
OnceFunc
,
OnceValue
,
OnceValues
sync.WaitGroup
等待goroutines完成
Add
需在
go
之前调用;Go 1.24+:
wg.Go()
简化了使用方式
x/sync/singleflight
去重并发调用防止缓存击穿
x/sync/errgroup
Goroutine组 + 错误处理
SetLimit(n)
可替代手动实现的工作池
如需详细示例和反模式说明,请查看同步原语深度解析

Concurrency Checklist

并发检查清单

Before spawning a goroutine, answer:
  • How will it exit? — context cancellation, channel close, or explicit signal
  • Can I signal it to stop? — pass
    context.Context
    or done channel
  • Can I wait for it?
    sync.WaitGroup
    or
    errgroup
  • Who owns the channels? — creator/sender owns and closes
  • Should this be synchronous instead? — don't add concurrency without measured need
在启动goroutine前,请确认:
  • 它将如何退出? —— context取消、channel关闭或显式信号
  • 能否向它发送停止信号? —— 传递
    context.Context
    或done channel
  • 能否等待它完成? —— 使用
    sync.WaitGroup
    errgroup
  • 谁拥有channels的所有权? —— 创建方/发送方拥有并负责关闭channel
  • 是否可以用同步方式替代? —— 若无明确需求,不要随意引入并发

Pipelines and Worker Pools

管道与工作池

For pipeline patterns (fan-out/fan-in, bounded workers, generator chains, Go 1.23+ iterators,
samber/ro
), see Pipelines and Worker Pools.
如需管道模式(扇出/扇入、有界工作者、生成器链、Go 1.23+迭代器、
samber/ro
)的相关内容,请查看管道与工作池

Parallelizing Concurrency Audits

并行化并发审计

When auditing concurrency across a large codebase, use up to 5 parallel sub-agents (Agent tool):
  1. Find all goroutine spawns (
    go func
    ,
    go method
    ) and verify shutdown mechanisms
  2. Search for mutable globals and shared state without synchronization
  3. Audit channel usage — ownership, direction, closure, buffer sizes
  4. Find
    time.After
    in loops, missing
    ctx.Done()
    in select, unbounded spawning
  5. Check mutex usage,
    sync.Map
    , atomics, and thread-safety documentation
当在大型代码库中审计并发代码时,可使用最多5个并行子代理(Agent工具):
  1. 查找所有goroutine启动点(
    go func
    ,
    go method
    )并验证关闭机制
  2. 搜索可变全局变量和未加同步的共享状态
  3. 审计channel的使用——所有权、方向、关闭方式、缓冲区大小
  4. 查找循环中的
    time.After
    、select中缺失的
    ctx.Done()
    、无限制的goroutine启动
  5. 检查互斥锁使用、
    sync.Map
    、原子操作以及线程安全文档

Common Mistakes

常见错误

MistakeFix
Fire-and-forget goroutineProvide stop mechanism (context, done channel)
Closing channel from receiverOnly the sender closes
time.After
in hot loop
Reuse
time.NewTimer
+
Reset
Missing
ctx.Done()
in select
Always select on context to allow cancellation
Unbounded goroutine spawningUse
errgroup.SetLimit(n)
or semaphore
Sharing pointer via channelSend copies or immutable values
wg.Add
inside goroutine
Call
Add
before
go
Wait
may return early otherwise
Forgetting
-race
in CI
Always run
go test -race ./...
Mutex held across I/OKeep critical sections short
常见错误修复方案
即发即弃的goroutine提供停止机制(context、done channel)
接收方关闭channel仅由发送方关闭channel
热循环中使用
time.After
复用
time.NewTimer
+
Reset
select中缺失
ctx.Done()
始终在select中监听context以支持取消
无限制启动goroutines使用
errgroup.SetLimit(n)
或信号量
通过channel传递指针发送副本或不可变值
在goroutine内部调用
wg.Add
go
之前调用
Add
——否则
Wait
可能提前返回
CI中忘记启用
-race
始终执行
go test -race ./...
持有互斥锁期间执行I/O尽量缩短临界区长度

Cross-References

交叉参考

  • -> See
    samber/cc-skills-golang@golang-performance
    skill for false sharing, cache-line padding,
    sync.Pool
    hot-path patterns
  • -> See
    samber/cc-skills-golang@golang-context
    skill for cancellation propagation and timeout patterns
  • -> See
    samber/cc-skills-golang@golang-safety
    skill for concurrent map access and race condition prevention
  • -> See
    samber/cc-skills-golang@golang-troubleshooting
    skill for debugging goroutine leaks and deadlocks
  • -> See
    samber/cc-skills-golang@golang-design-patterns
    skill for graceful shutdown patterns
  • -> 如需伪共享、缓存行填充、
    sync.Pool
    热路径模式相关内容,请查看
    samber/cc-skills-golang@golang-performance
    skill
  • -> 如需取消传播和超时模式相关内容,请查看
    samber/cc-skills-golang@golang-context
    skill
  • -> 如需并发map访问和竞争条件预防相关内容,请查看
    samber/cc-skills-golang@golang-safety
    skill
  • -> 如需调试goroutine泄漏和死锁相关内容,请查看
    samber/cc-skills-golang@golang-troubleshooting
    skill
  • -> 如需优雅关闭模式相关内容,请查看
    samber/cc-skills-golang@golang-design-patterns
    skill

References

参考资料