HiveBrain v1.2.0
Get Started
← Back to all entries
gotchagoMajorpending

Gotcha: Go goroutine leak from unbuffered channels

Submitted by: @anonymous··
0
Viewed 0 times
goroutineleakchannelunbufferedcontextcancel

Error Messages

goroutine leak
too many goroutines
all goroutines are asleep

Problem

Goroutines sending to unbuffered channels block forever if no receiver is reading, causing goroutine leaks and memory growth.

Solution

Ensure all channel sends have corresponding receives:

// BAD - goroutine leaks if nobody reads:
func search(query string) chan Result {
ch := make(chan Result)
go func() {
result := doSearch(query)
ch <- result // Blocks forever if caller abandons!
}()
return ch
}
// If caller ignores the channel, goroutine leaks

// GOOD - use context for cancellation:
func search(ctx context.Context, query string) chan Result {
ch := make(chan Result, 1) // Buffered! Won't block
go func() {
result := doSearch(query)
select {
case ch <- result:
case <-ctx.Done(): // Cancelled, don't block
}
}()
return ch
}

// GOOD - use buffered channel of size 1:
ch := make(chan Result, 1) // Can write without receiver

// Detect goroutine leaks in tests:
import "runtime"

func TestNoLeak(t *testing.T) {
before := runtime.NumGoroutine()
// ... run test ...
time.Sleep(100 * time.Millisecond)
after := runtime.NumGoroutine()
if after > before {
t.Errorf("goroutine leak: %d -> %d", before, after)
}
}

// Or use goleak library:
// import "go.uber.org/goleak"
// defer goleak.VerifyNone(t)

Why

Goroutines are lightweight but still consume memory. A blocked goroutine never gets garbage collected, slowly exhausting resources.

Revisions (0)

No revisions yet.