SuperAPI AI Directory

golang-goroutines

Coding & development

View repository

Updated 10-15 20:31

About this skill

Golang Goroutines

Every goroutine needs an owner, an end, and an error path. Prefer context for cancellation over ad hoc signals.

When to use

  • Parallelize independent I/O (fan-out) with a concurrency limit.
  • Pipelines with clear stages and backpressure.
  • Background work tied to the lifecycle of the process or the request.
  • Do not use a goroutine to “let the handler return faster” without lifecycle ownership.

Non-negotiable rules

  • Derive contexts: context.WithCancel / WithTimeout / WithDeadline from the parent.
  • Cancel and Wait (or equivalent) before returning in functions that spawn work.
  • Never ignore the return value of errors from child goroutines.
  • Channels: define who closes; receivers do not close what they do not own.
  • Shared data: mutex, channels, or concurrent structures — choose one strategy, not both blindly.
  • No go func() in a loop capturing an iteration variable without explicit binding (v := v).

Context and errgroup

g, ctx := errgroup.WithContext(ctx)
g.SetLimit(8)

for _, id := range ids {
    id := id
    g.Go(func() error {
        return fetchOne(ctx, id)
    })
}

if err := g.Wait(); err != nil {
    return err
}
  • golang.org/x/sync/errgroup for fan-out with cancellation on the first error.
  • SetLimit to avoid a goroutine explosion.
  • Propagate the same ctx all the way to I/O (DB, HTTP, disk).

Worker pool

jobs := make(chan Job)
var wg sync.WaitGroup

for i := 0; i < workers; i++ {
    wg.Add(1)
    go func() {
        defer wg.Done()
        for job := range jobs {
            process(ctx, job)
        }
    }()
}

for _, job := range batch {
    select {
    case <-ctx.Done():
        close(jobs)
        wg.Wait()
        return ctx.Err()
    case jobs <- job:
    }
}
close(jobs)
wg.Wait()
  • Buffer only with a reason (decouple rhythms); unbounded growth is a bug.
  • Shutdown: stop sending → close the jobs channel → Wait workers.

Channels and select

  • Prefer communication via channels for ownership handoff.
  • select with ctx.Done() on sends/receives that can block.
  • Avoid a notification channel + confusing boolean; use close or context.
  • Fan-in: one goroutine per source or errgroup, explicit merge.

sync

Primitive Use
Mutex / RWMutex Short shared state
WaitGroup Wait for N workers
Once Idempotent init
Map / atomics Specific cases; measure before optimizing
  • Hold the lock for as little time as possible; do not do I/O under a mutex.
  • Document invariants of the protected state.

Leaks and races

  • Symptoms: growing goroutines, flaky tests, shutdown that does not finish.
  • Tools: go test -race, pprof goroutine, timeouts in tests.
  • Each go must have an exit condition tied to context, channel close, or WaitGroup.

Anti-patterns

  • Goroutine without cancellation or join
  • time.Sleep as coordination
  • Fire-and-forget in an HTTP request without tracking
  • Closing the channel on the receiver side
  • Mutex + channel for the same coordination without need
  • Ignoring ctx.Err() after select
  • Pool without a limit under load

Completion criteria

  • Every goroutine has a clear lifecycle (start/stop)
  • Context propagated and respected
  • Errors from child goroutines aggregated or handled
  • No data races (-race clean in the areas touched)
  • Backpressure or concurrency limit where there is fan-out
  • Shutdown does not leave orphaned work