go-concurrency

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Go Concurrency Skill

Go并发技能

Operator Context

操作上下文

This skill operates as an operator for Go concurrency workflows, configuring Claude's behavior for correct, leak-free concurrent code. It implements the Domain Intelligence architectural pattern -- encoding Go concurrency idioms, sync primitives, and channel patterns as non-negotiable constraints rather than suggestions.
本技能作为Go并发工作流的操作器,配置Claude的行为以生成正确、无泄漏的并发代码。它实现了领域智能架构模式——将Go并发惯用写法、同步原语和通道模式编码为不可协商的约束而非建议。

Hardcoded Behaviors (Always Apply)

硬编码行为(始终适用)

  • CLAUDE.md Compliance: Read and follow repository CLAUDE.md before writing concurrent code
  • Over-Engineering Prevention: Use concurrency only when justified by I/O, CPU parallelism, or measured bottleneck. Sequential code is correct by default
  • Context First Parameter: All cancellable or I/O operations accept
    context.Context
    as first parameter
  • No Goroutine Leaks: Every goroutine must have a guaranteed exit path via context, channel close, or explicit shutdown
  • Race Detector Required: Run
    go test -race
    on all concurrent code during development
  • Channel Ownership: Only the sender closes a channel. Never close from receiver side
  • Select With Context: Every
    select
    statement in concurrent code must include a
    <-ctx.Done()
    case
  • 遵循CLAUDE.md规范:编写并发代码前阅读并遵循仓库中的CLAUDE.md
  • 避免过度设计:仅当I/O、CPU并行性或已测量到瓶颈时才使用并发。顺序代码默认是正确的
  • 上下文作为首个参数:所有可取消或I/O操作都接受
    context.Context
    作为首个参数
  • 无Goroutine泄漏:每个goroutine必须通过上下文、通道关闭或显式关闭拥有明确的退出路径
  • 必须使用竞态检测器:开发期间对所有并发代码运行
    go test -race
  • 通道所有权规则:仅发送方可以关闭通道,绝不能从接收方侧关闭
  • Select语句需包含上下文:并发代码中的每个
    select
    语句必须包含
    <-ctx.Done()
    分支

Default Behaviors (ON unless disabled)

默认行为(启用状态,除非手动禁用)

  • errgroup Over WaitGroup: Prefer
    golang.org/x/sync/errgroup
    for goroutine management with error collection
  • Buffered Channel Sizing: Buffer size matches expected backpressure, not arbitrary large numbers
  • Directional Channel Returns: Return
    <-chan T
    (receive-only) from producer functions to prevent caller misuse
  • Mutex Scope Minimization: Lock only the critical section, use
    defer Unlock()
    immediately after
    Lock()
  • Loop Variable Safety: Use Go 1.22+ loop variable semantics; remove legacy
    item := item
    shadows in new code
  • Graceful Shutdown: Workers and servers implement clean shutdown with drain timeout
  • Atomic for Counters: Use
    atomic.Int64
    /
    atomic.Value
    for simple shared counters instead of mutex
  • 优先使用errgroup而非WaitGroup:对于带错误收集的goroutine管理,优先使用
    golang.org/x/sync/errgroup
  • 缓冲通道大小设置:缓冲区大小匹配预期的背压,而非随意设置大数值
  • 返回定向通道:生产者函数返回
    <-chan T
    (仅接收)类型,防止调用方误用
  • 最小化互斥锁作用域:仅锁定临界区,在
    Lock()
    后立即使用
    defer Unlock()
  • 循环变量安全:使用Go 1.22+的循环变量语义;在新代码中移除传统的
    item := item
    阴影写法
  • 优雅关闭:工作协程和服务器实现带排空超时的优雅关闭
  • 原子操作实现计数器:对于简单的共享计数器,使用
    atomic.Int64
    /
    atomic.Value
    而非互斥锁

Optional Behaviors (OFF unless enabled)

可选行为(禁用状态,除非手动启用)

  • Gopls MCP Analysis: Use gopls MCP tools to trace channel usage and context propagation —
    go_symbol_references
    for tracing channel flow,
    go_file_context
    for understanding goroutine spawn sites,
    go_diagnostics
    after concurrent code edits. Fallback:
    gopls references
    CLI or LSP tool
  • Container GOMAXPROCS Tuning: Configure GODEBUG flags for container CPU limit overrides
  • Performance Profiling: Profile goroutine counts and channel contention under load
  • Custom Rate Limiter: Build token-bucket rate limiter instead of using
    golang.org/x/time/rate
  • Gopls MCP分析:使用gopls MCP工具追踪通道使用和上下文传播——
    go_symbol_references
    用于追踪通道流,
    go_file_context
    用于理解goroutine启动位置,
    go_diagnostics
    用于并发代码编辑后的诊断。备选方案:
    gopls references
    CLI或LSP工具
  • 容器GOMAXPROCS调优:配置GODEBUG标志以覆盖容器CPU限制
  • 性能分析:在负载下分析goroutine数量和通道竞争情况
  • 自定义速率限制器:构建令牌桶速率限制器,而非使用
    golang.org/x/time/rate

What This Skill CAN Do

本技能可完成的工作

  • Guide implementation of worker pools, fan-out/fan-in, and pipeline patterns
  • Apply correct context propagation through concurrent call chains
  • Select appropriate sync primitives (Mutex, RWMutex, WaitGroup, Once, atomic)
  • Implement rate limiting with context-aware waiting
  • Diagnose and fix race conditions, deadlocks, and goroutine leaks
  • Structure graceful shutdown for background workers and servers
  • 指导实现工作池、扇出/扇入和流水线模式
  • 在并发调用链中正确传播上下文
  • 选择合适的同步原语(Mutex、RWMutex、WaitGroup、Once、atomic)
  • 实现支持上下文感知等待的速率限制
  • 诊断并修复竞态条件、死锁和goroutine泄漏
  • 为后台工作协程和服务器设计优雅关闭逻辑

What This Skill CANNOT Do

本技能无法完成的工作

  • Fix general Go bugs unrelated to concurrency (use systematic-debugging instead)
  • Optimize non-concurrent performance (use performance optimization workflows instead)
  • Write tests for concurrent code (use go-testing skill instead)
  • Handle Go error handling patterns (use go-error-handling skill instead)

  • 修复与并发无关的通用Go bug(请改用systematic-debugging技能)
  • 优化非并发场景的性能(请改用性能优化工作流)
  • 为并发代码编写测试(请改用go-testing技能)
  • 处理Go错误处理模式(请改用go-error-handling技能)

Instructions

操作步骤

Step 1: Assess Concurrency Need

步骤1:评估并发需求

Before writing concurrent code, answer these questions:
  1. Is the work I/O-bound? (network, database, filesystem) -- concurrency likely helps
  2. Is the work CPU-bound? -- concurrency helps only if parallelizable
  3. Is there a measured bottleneck? -- if not measured, don't assume
If none apply, write sequential code. Concurrency adds complexity; justify it.
编写并发代码前,请回答以下问题:
  1. 工作是否为I/O密集型?(网络、数据库、文件系统)——并发通常有帮助
  2. 工作是否为CPU密集型?——仅当可并行化时并发才有帮助
  3. 是否存在已测量的瓶颈?——若未测量,请勿假设
如果以上都不满足,请编写顺序代码。并发会增加复杂度,必须有合理理由。

Step 2: Choose the Right Primitive

步骤2:选择合适的原语

NeedPrimitiveWhen
Communicate between goroutinesChannelData flows from producer to consumer
Protect shared state
sync.Mutex
Multiple goroutines read/write same data
Read-heavy shared state
sync.RWMutex
Many readers, few writers
Wait for goroutines to finish
errgroup.Group
Need error collection + context cancel
Wait without error collection
sync.WaitGroup
Fire-and-forget goroutines
One-time initialization
sync.Once
Lazy singleton, config loading
Simple shared counter
atomic.Int64
Increment/read without mutex overhead
需求原语适用场景
在goroutine间通信Channel数据从生产者流向消费者
保护共享状态
sync.Mutex
多个goroutine读写同一数据
读密集型共享状态
sync.RWMutex
大量读操作,少量写操作
等待goroutine完成
errgroup.Group
需要错误收集+上下文取消
无需错误收集的等待
sync.WaitGroup
无需关注结果的goroutine
一次性初始化
sync.Once
懒加载单例、配置加载
简单共享计数器
atomic.Int64
无互斥锁开销的递增/读取操作

Step 3: Context Propagation

步骤3:上下文传播

Always pass context as first parameter for I/O or cancellable operations.
go
func FetchData(ctx context.Context, id string) (*Data, error) {
    ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
    defer cancel()

    resultCh := make(chan *Data, 1)
    errCh := make(chan error, 1)

    go func() {
        data, err := slowOperation(id)
        if err != nil {
            errCh <- err
            return
        }
        resultCh <- data
    }()

    select {
    case data := <-resultCh:
        return data, nil
    case err := <-errCh:
        return nil, fmt.Errorf("fetch failed: %w", err)
    case <-ctx.Done():
        return nil, fmt.Errorf("fetch cancelled: %w", ctx.Err())
    }
}
When to use context vs not:
go
// USE context: I/O, cancellable operations, request-scoped values
func FetchUserData(ctx context.Context, userID string) (*User, error) { ... }

// NO context needed: pure computation
func CalculateTotal(prices []float64) float64 { ... }
对于I/O或可取消操作,始终将上下文作为首个参数传递。
go
func FetchData(ctx context.Context, id string) (*Data, error) {
    ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
    defer cancel()

    resultCh := make(chan *Data, 1)
    errCh := make(chan error, 1)

    go func() {
        data, err := slowOperation(id)
        if err != nil {
            errCh <- err
            return
        }
        resultCh <- data
    }()

    select {
    case data := <-resultCh:
        return data, nil
    case err := <-errCh:
        return nil, fmt.Errorf("fetch failed: %w", err)
    case <-ctx.Done():
        return nil, fmt.Errorf("fetch cancelled: %w", ctx.Err())
    }
}
何时使用上下文,何时不使用:
go
// 使用context:I/O操作、可取消操作、请求作用域的值
func FetchUserData(ctx context.Context, userID string) (*User, error) { ... }

// 无需context:纯计算操作
func CalculateTotal(prices []float64) float64 { ... }

Step 4: Implement the Pattern

步骤4:实现模式

Sync Primitives
go
// Mutex for state protection
type SafeCounter struct {
    mu    sync.Mutex
    count int
}

func (c *SafeCounter) Increment() {
    c.mu.Lock()
    defer c.mu.Unlock()
    c.count++
}

// RWMutex for read-heavy workloads
type Cache struct {
    mu    sync.RWMutex
    items map[string]any
}

func (c *Cache) Get(key string) (any, bool) {
    c.mu.RLock()
    defer c.mu.RUnlock()
    item, ok := c.items[key]
    return item, ok
}

func (c *Cache) Set(key string, value any) {
    c.mu.Lock()
    defer c.mu.Unlock()
    c.items[key] = value
}
errgroup for concurrent work with error handling (preferred over WaitGroup)
go
import "golang.org/x/sync/errgroup"

func ProcessAll(ctx context.Context, items []Item) error {
    g, ctx := errgroup.WithContext(ctx)

    for _, item := range items {
        g.Go(func() error {
            return process(ctx, item)  // Go 1.22+: item captured correctly
        })
    }

    return g.Wait()
}
sync.Once for one-time initialization
go
type Config struct {
    once   sync.Once
    config *AppConfig
    err    error
}

func (c *Config) Load() (*AppConfig, error) {
    c.once.Do(func() {
        c.config, c.err = loadConfigFromFile()
    })
    return c.config, c.err
}
Channel patterns: buffered vs unbuffered
go
// Unbuffered: synchronous, sender blocks until receiver ready
ch := make(chan int)

// Buffered: async up to buffer size
ch := make(chan int, 100)

// Guidelines:
// - Use unbuffered when you need synchronization
// - Use buffered to decouple sender/receiver timing
// - Buffer size should match expected backpressure
For worker pool, fan-out/fan-in, pipeline, rate limiter, and graceful shutdown patterns, see
references/concurrency-patterns.md
.
同步原语
go
// Mutex用于状态保护
type SafeCounter struct {
    mu    sync.Mutex
    count int
}

func (c *SafeCounter) Increment() {
    c.mu.Lock()
    defer c.mu.Unlock()
    c.count++
}

// RWMutex用于读密集型工作负载
type Cache struct {
    mu    sync.RWMutex
    items map[string]any
}

func (c *Cache) Get(key string) (any, bool) {
    c.mu.RLock()
    defer c.mu.RUnlock()
    item, ok := c.items[key]
    return item, ok
}

func (c *Cache) Set(key string, value any) {
    c.mu.Lock()
    defer c.mu.Unlock()
    c.items[key] = value
}
errgroup用于带错误处理的并发工作(优先于WaitGroup)
go
import "golang.org/x/sync/errgroup"

func ProcessAll(ctx context.Context, items []Item) error {
    g, ctx := errgroup.WithContext(ctx)

    for _, item := range items {
        g.Go(func() error {
            return process(ctx, item)  // Go 1.22+: 正确捕获item变量
        })
    }

    return g.Wait()
}
sync.Once用于一次性初始化
go
type Config struct {
    once   sync.Once
    config *AppConfig
    err    error
}

func (c *Config) Load() (*AppConfig, error) {
    c.once.Do(func() {
        c.config, c.err = loadConfigFromFile()
    })
    return c.config, c.err
}
通道模式:缓冲与非缓冲
go
// 非缓冲:同步,发送方阻塞直到接收方就绪
ch := make(chan int)

// 缓冲:异步,最多容纳指定数量的元素
ch := make(chan int, 100)

// 指南:
// - 需要同步时使用非缓冲通道
// - 使用缓冲通道解耦发送方/接收方的时序
// - 缓冲区大小应匹配预期的背压
关于工作池、扇出/扇入、流水线、速率限制器和优雅关闭模式,请参考
references/concurrency-patterns.md

Step 5: Run Race Detector

步骤5:运行竞态检测器

bash
undefined
bash
undefined

ALWAYS run with race detector during development

开发期间务必运行竞态检测器

go test -race -count=1 -v ./...
go test -race -count=1 -v ./...

Run specific test with race detection

对特定测试启用竞态检测

go test -race -run TestConcurrentOperation ./...
undefined
go test -race -run TestConcurrentOperation ./...
undefined

Step 6: Concurrency Checklist

步骤6:并发检查清单

Before declaring concurrent code complete, verify:
  • Context propagation - All I/O operations accept context
  • Goroutine exit paths - Every goroutine can terminate
  • Channel closure - Channels closed by sender only
  • Select with context - All selects include
    <-ctx.Done()
  • Proper synchronization - Shared state protected
  • Race detector passes -
    go test -race
    clean
  • Graceful shutdown - Workers stop cleanly
  • No goroutine leaks - All goroutines tracked

在确认并发代码完成前,请验证以下项:
  • 上下文传播 - 所有I/O操作都接受上下文
  • Goroutine退出路径 - 每个goroutine都能终止
  • 通道关闭 - 仅发送方关闭通道
  • Select语句包含上下文 - 所有select语句都包含
    <-ctx.Done()
  • 正确同步 - 共享状态已被保护
  • 竞态检测器通过 -
    go test -race
    无报错
  • 优雅关闭 - 工作协程能干净停止
  • 无Goroutine泄漏 - 所有goroutine都被追踪

Error Handling

错误处理

Error: "DATA RACE detected by race detector"

错误:"DATA RACE detected by race detector"

Cause: Multiple goroutines access shared variable without synchronization Solution:
  1. Identify the variable from the race detector output (it shows goroutine stacks)
  2. Protect with
    sync.Mutex
    for complex state, or
    atomic
    for simple counters
  3. If using channels, ensure the variable is only accessed by one goroutine at a time
  4. Re-run
    go test -race
    to confirm fix
原因:多个goroutine在无同步的情况下访问共享变量 解决方案:
  1. 从竞态检测器输出中定位变量(会显示goroutine栈信息)
  2. 对于复杂状态,使用
    sync.Mutex
    保护;对于简单计数器,使用
    atomic
    操作
  3. 如果使用通道,确保变量同一时间仅被一个goroutine访问
  4. 重新运行
    go test -race
    确认修复

Error: "all goroutines are asleep - deadlock!"

错误:"all goroutines are asleep - deadlock!"

Cause: Circular wait on channels or mutexes; no goroutine can make progress Solution:
  1. Check for unbuffered channel sends with no receiver ready
  2. Check for mutex lock ordering inconsistencies
  3. Ensure channels are closed when done to unblock
    range
    loops
  4. Add buffering to channels where appropriate
原因:通道或互斥锁上的循环等待;没有goroutine能继续执行 解决方案:
  1. 检查是否存在无接收方就绪的非缓冲通道发送操作
  2. 检查互斥锁的锁定顺序是否一致
  3. 确保通道在使用完毕后关闭,以解除
    range
    循环的阻塞
  4. 为通道添加适当的缓冲

Error: "context deadline exceeded" in concurrent operations

错误:并发操作中出现"context deadline exceeded"

Cause: Operations not completing within timeout, or context cancelled upstream Solution:
  1. Check if timeout is realistic for the operation
  2. Verify context is propagated correctly (not using
    context.Background()
    when a parent context exists)
  3. Ensure goroutines check
    ctx.Done()
    in their select loops
  4. Consider increasing timeout or adding per-operation timeouts with
    context.WithTimeout

原因:操作未在超时时间内完成,或上游上下文被取消 解决方案:
  1. 检查超时时间对于操作是否合理
  2. 验证上下文是否正确传播(当存在父上下文时,不要使用
    context.Background()
  3. 确保goroutine在其select循环中检查
    ctx.Done()
  4. 考虑增加超时时间,或使用
    context.WithTimeout
    为每个操作设置单独超时

Anti-Patterns

反模式

Anti-Pattern 1: Goroutine Without Exit Path

反模式1:无退出路径的Goroutine

What it looks like:
go func() { for { doWork() } }()
with no context check or stop channel Why wrong: Goroutine runs forever, leaking memory. Cannot be cancelled or shut down gracefully. Do instead: Always include
<-ctx.Done()
or a stop channel in goroutine loops.
表现形式
go func() { for { doWork() } }()
无上下文检查或停止通道 错误原因:Goroutine会永久运行,导致内存泄漏,无法被取消或优雅关闭 正确做法:Goroutine循环中始终包含
<-ctx.Done()
或停止通道分支

Anti-Pattern 2: Unnecessary Concurrency

反模式2:不必要的并发

What it looks like: Spawning goroutines for sequential work that does not benefit from parallelism Why wrong: Adds complexity (error channels, WaitGroups, race risks) without performance gain. Sequential code is simpler and correct by default. Do instead: Measure first. Use concurrency only for I/O-bound, CPU-parallel, or proven bottleneck scenarios.
表现形式:为无法从并行中获益的顺序工作启动goroutine 错误原因:无性能提升却增加了复杂度(错误通道、WaitGroups、竞态风险)。顺序代码更简单且默认正确 正确做法:先测量性能。仅在I/O密集型、CPU并行型或已证实的瓶颈场景中使用并发

Anti-Pattern 3: Closing Channel From Receiver Side

反模式3:从接收方侧关闭通道

What it looks like: Consumer goroutine calling
close(ch)
on a channel it reads from Why wrong: Sender may still send, causing panic. Multiple receivers may double-close. Do instead: Only the sender (producer) closes the channel. Use
defer close(ch)
in the goroutine that writes.
表现形式:消费者goroutine调用
close(ch)
关闭其读取的通道 错误原因:发送方可能仍在发送数据,导致panic;多个接收方可能重复关闭通道 正确做法:仅发送方(生产者)可以关闭通道。在写入的goroutine中使用
defer close(ch)

Anti-Pattern 4: Mutex Lock Without Defer Unlock

反模式4:互斥锁锁定后未使用defer Unlock

What it looks like:
mu.Lock()
followed by complex logic before
mu.Unlock()
, with early returns in between Why wrong: Early returns or panics skip the
Unlock()
, causing deadlocks. Do instead: Always
defer mu.Unlock()
immediately after
mu.Lock()
.
表现形式
mu.Lock()
后执行复杂逻辑再调用
mu.Unlock()
,中间存在提前返回 错误原因:提前返回或panic会跳过
Unlock()
,导致死锁 正确做法:在
Lock()
后立即使用
defer mu.Unlock()

Anti-Pattern 5: Ignoring Context in Select

反模式5:Select语句忽略上下文

What it looks like:
select { case msg := <-ch: handle(msg) }
without a
<-ctx.Done()
case Why wrong: Goroutine blocks forever if channel never receives and context is cancelled. Do instead: Every
select
in concurrent code must include
case <-ctx.Done(): return
.

表现形式
select { case msg := <-ch: handle(msg) }
不包含
<-ctx.Done()
分支 错误原因:如果通道从未接收数据且上下文被取消,goroutine会永久阻塞 正确做法:并发代码中的每个
select
语句必须包含
case <-ctx.Done(): return

References

参考资料

This skill uses these shared patterns:
  • Anti-Rationalization - Prevents shortcut rationalizations
  • Verification Checklist - Pre-completion checks
本技能使用以下共享模式:
  • Anti-Rationalization - 防止捷径式合理化
  • Verification Checklist - 完成前检查

Domain-Specific Anti-Rationalization

领域特定的反合理化

RationalizationWhy It's WrongRequired Action
"No need for context, this is fast"Fast today, slow tomorrow under loadPass context to all I/O operations
"Race detector is slow, skip it"Races are silent until productionRun
go test -race
every time
"One goroutine leak won't matter"Leaks compound; OOM in productionVerify every goroutine has exit path
"Sequential is too slow"Assumption without measurementProfile first, then add concurrency
"Buffer of 1000 should be enough"Arbitrary buffers hide backpressure bugsSize buffers to actual throughput
合理化借口错误原因要求操作
"不需要上下文,这个操作很快"现在快但未来负载下可能变慢为所有I/O操作传递上下文
"竞态检测器太慢,跳过它"竞态条件在生产环境才会显现每次都运行
go test -race
"一个Goroutine泄漏没关系"泄漏会累积,最终导致OOM验证每个goroutine都有退出路径
"顺序代码太慢了"无测量依据的假设先分析性能,再添加并发
"缓冲区设为1000应该足够"随意设置的缓冲区会隐藏背压bug缓冲区大小匹配实际吞吐量

Reference Files

参考文件

  • ${CLAUDE_SKILL_DIR}/references/concurrency-patterns.md
    : Worker pool, fan-out/fan-in, pipeline, rate limiter, and graceful shutdown patterns with full code examples
  • ${CLAUDE_SKILL_DIR}/references/concurrency-patterns.md
    :包含工作池、扇出/扇入、流水线、速率限制器和优雅关闭模式的完整代码示例