关于 golang-concurrency
golang-concurrency 是面向 Go 开发的编码技能,涵盖并发代码的编写、评审和审计。它侧重 goroutine 生命周期、所有权、取消机制与结构化错误处理,并提供大型代码库的并行审计方法。
主要功能
- 01编写、评审和审计模式
- 02goroutine 生命周期与泄漏预防
- 03通道所有权和同步指导
- 04工作池与流水线模式
- 05最多五个子 Agent 并行审计
适用场景
- 01检查 Pull Request 中的并发缺陷
- 02审计代码库中的 goroutine 退出机制
- 03实现可取消的工作池和流水线
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ samber/ cc-skills-golang --skill golang-concurrency安装后在 Claude Code 中输入/golang-concurrency确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
evals/evals.jsonreferences/channels-and-select.mdpipelines.mdsync-primitives.md详细介绍
角色: 你是一名 Go 并发工程师。你认为每个 goroutine 在证明其必要之前都是一种负担——正确性和无泄漏优先于性能。
编排模式: 按照“并行化并发审计”一节中的说明,分派 5 个子 Agent,审计大型代码库中的并发代码,并将它们的发现汇总成一份报告。在 Claude Code 中,使用 ultracode 明确启用多 Agent 编排。
模式:
- 编写模式 — 实现并发代码(goroutine、channel、同步原语、工作池、流水线)。遵循下方的顺序说明。
- 审查模式 — 审查 PR 中的并发代码变更。聚焦于差异:检查 goroutine 泄漏、缺少 context 传递、所有权违规以及未受保护的共享状态。按顺序进行。
- 审计模式 — 审计整个代码库中现有的并发代码。按照“并行化并发审计”一节中的说明,最多使用 5 个并行子 Agent。
社区默认规则。 如果公司的技能明确取代
samber/cc-skills-golang@golang-concurrency技能,则优先采用该公司技能。
Go 并发最佳实践
Go 的并发模型基于 goroutine 和 channel。Goroutine 开销很低,但并非没有开销——每启动一个 goroutine,你都必须管理一个资源。目标是实现结构化并发:每个 goroutine 都有明确的所有者、可预期的退出方式,以及恰当的错误传递机制。
核心原则
- 每个 goroutine 都必须有明确的退出方式 — 没有关闭机制(context、done channel、WaitGroup),goroutine 就会泄漏并不断累积,直至进程崩溃
- 通过通信共享内存 — channel 可明确转移所有权;互斥锁能保护共享状态,但所有权变得隐式
- 通过 channel 发送副本,而不是指针 — 发送指针会产生不可见的共享内存,违背使用 channel 的初衷
- 只有发送方才能关闭 channel — 如果发送方在关闭后写入,从接收方一侧关闭就会导致 panic
- 指定 channel 方向(
chan<-、<-chan)— 编译器会在构建时阻止误用 - 默认使用无缓冲 channel — 较大的缓冲区会掩盖背压;只有经过测量并确认有必要时才使用
- 始终在 select 中包含
ctx.Done()— 否则,调用方取消后 goroutine 会泄漏 - 避免在热点循环中反复使用
time.After— 每次调用都会分配一个计时器并造成不必要的开销;对于长期运行的循环,使用time.NewTimer+Reset - 在测试中使用
go.uber.org/goleak跟踪 goroutine 泄漏
有关 channel/select 的详细代码示例,请参阅 Channels and Select Patterns。
Channel、Mutex 还是 Atomic
| 场景 | 使用 | 原因 |
|---|---|---|
| 在 goroutine 之间传递数据 | Channel | 明确传递所有权 |
| 协调 goroutine 的生命周期 | Channel + context | 通过 select 实现干净的关闭 |
| 保护共享结构体字段 | sync.Mutex / sync.RWMutex | 简单的临界区 |
| 简单计数器、标志 | sync/atomic | 无锁,开销更低 |
| 多读少写的 map | sync.Map | 针对读密集型工作负载进行了优化。并发读写 map 会导致进程直接崩溃 |
| 缓存开销高昂的计算结果 | sync.Once / singleflight | 只执行一次或进行去重 |
WaitGroup 还是 errgroup
| 需求 | 使用 | 原因 |
|---|---|---|
| 等待 goroutine 完成,不需要错误 | sync.WaitGroup | 即发即弃 |
| 等待并收集第一个错误 | errgroup.Group | 传递错误 |
| 等待,并在首次出错时取消其他 goroutine | errgroup.WithContext | 出错时取消 context |
| 等待并限制并发数 | errgroup.SetLimit(n) | 内置工作池 |
同步原语速查
| 原语 | 用途 | 关键说明 |
|---|---|---|
sync.Mutex | 保护共享状态 | 尽量缩短临界区;切勿在 I/O 期间持有锁 |
sync.RWMutex | 读多写少 | 切勿将 RLock 升级为 Lock(会导致死锁) |
sync/atomic | 简单计数器、标志 | 优先使用类型化原子操作(Go 1.19+):atomic.Int64、atomic.Bool |
sync.Map | 并发映射,读操作密集 | 无需显式加锁;写操作占主导时,使用 RWMutex+map |
sync.Pool | 重用临时对象 | 调用 Put() 前务必先调用 Reset();可减少 GC 压力 |
sync.Once | 一次性初始化 | Go 1.21+:OnceFunc、OnceValue、OnceValues |
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) 可替代手动实现的工作池 |
如需详细示例和反模式,请参阅 同步原语深入解析。
并发检查清单
启动 goroutine 前,请先回答:
- 它将如何退出? — 通过 context 取消、关闭 channel 或显式信号
- 我能否通知它停止? — 传入
context.Context或 done channel - 我能否等待它? — 使用
sync.WaitGroup或errgroup - 谁拥有这些 channel? — 创建者/发送方拥有并负责关闭
- 改为同步执行是否更合适? — 未经实际测量,不要增加并发
流水线和工作池
如需了解流水线模式(扇出/扇入、有界工作线程、生成器链、Go 1.23+ 迭代器、samber/ro),请参阅 流水线和工作池。
并行执行并发审查
审查大型代码库中的并发时,最多使用 5 个并行子 Agent:
- 查找所有启动 goroutine 的位置(
go func、go method),并验证关闭机制 - 搜索缺少同步保护的可变全局变量和共享状态
- 审查 channel 的使用情况——所有权、方向、关闭方式、缓冲区大小
- 查找循环中的
time.After、select中缺失的ctx.Done()、无界启动 goroutine - 检查互斥锁的使用、
sync.Map、原子操作和线程安全文档
常见错误
| 错误 | 修正方法 |
|---|---|
| 启动后不管的 goroutine | 提供停止机制(context、done channel) |
| 接收方关闭 channel | 只有发送方负责关闭 |
热循环中使用 time.After | 重用 time.NewTimer 并调用 Reset |
select 中缺少 ctx.Done() | 始终在 select 中监听 context,以便支持取消 |
| 无界启动 goroutine | 使用 errgroup.SetLimit(n) 或信号量 |
| 通过 channel 共享指针 | 发送副本或不可变值 |
在 goroutine 内调用 wg.Add | 在 go 之前调用 Add——否则 Wait 可能提前返回 |
忘记在 CI 中使用 -race | 始终运行 go test -race ./... |
| 在 I/O 期间持有互斥锁 | 尽量缩短临界区 |
交叉引用
- → 关于错误共享、缓存行填充和
sync.Pool热路径模式,请参阅samber/cc-skills-golang@golang-performanceskill - → 关于取消传播和超时模式,请参阅
samber/cc-skills-golang@golang-contextskill - → 关于并发映射访问和竞态条件防护,请参阅
samber/cc-skills-golang@golang-safetyskill - → 关于调试 goroutine 泄漏和死锁,请参阅
samber/cc-skills-golang@golang-troubleshootingskill - → 关于优雅关闭模式,请参阅
samber/cc-skills-golang@golang-design-patternsskill - → 关于在 CI 中使用这些指南进行自动化 AI 驱动代码审查,请参阅
samber/cc-skills-golang@golang-continuous-integrationskill
Goroutine 泄漏分析
Goroutine 泄漏分析(在 Go 1.26 中通过 GOEXPERIMENT=goroutineleakprofile 实验性启用)自 Go 1.27 起已在 runtime/pprof 中正式提供,无需构建标志。与下方现有工具配合使用时,它是一个适用于生产环境的泄漏信号。
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 ./...