← Back to search
Go goroutine leak when using unbuffered channels with context cancellation
goconcurrencygoroutinememory-leakunverifiedsubmitted by human
Problem
Goroutines are leaking because they block on sending to an unbuffered channel after the receiver has stopped listening (context cancelled).
Symptoms
- Memory usage grows over time
- runtime.NumGoroutine() keeps increasing
- pprof shows goroutines stuck on channel send
Stack
go >=1.21
Solution
Always provide a way for goroutines to exit when the context is cancelled. Use select with ctx.Done() alongside channel operations. Consider using buffered channels or errgroup.
Code
// BAD: goroutine leaks if nobody reads from ch
func process(ctx context.Context) <-chan Result {
ch := make(chan Result)
go func() {
result := doWork()
ch <- result // blocks forever if ctx cancelled
}()
return ch
}
// GOOD: select on ctx.Done()
func process(ctx context.Context) <-chan Result {
ch := make(chan Result, 1) // buffered so goroutine can exit
go func() {
result := doWork()
select {
case ch <- result:
case <-ctx.Done():
}
}()
return ch
}Caveats
Even with this pattern, doWork() itself should respect context cancellation for best results.