How to Prevent Goroutine Leak in Go: Causes and Fixes
To prevent
goroutine leaks in Go, always ensure that goroutines can exit by properly handling cancellation signals like context.Context or closing channels they listen to. Avoid blocking operations without timeouts or exit conditions inside goroutines to stop them from running forever.Why This Happens
Goroutine leaks happen when a goroutine keeps running but is no longer needed, often because it waits forever on a channel or blocking operation that never completes. This wastes memory and CPU, slowing down your program.
go
package main import ( "fmt" "time" ) func worker(ch chan int) { for { // Waiting forever if no data comes val := <-ch fmt.Println("Received:", val) } } func main() { ch := make(chan int) go worker(ch) // main does not send anything and exits time.Sleep(1 * time.Second) fmt.Println("Main done") }
Output
Main done
The Fix
Use a context.Context to signal the goroutine to stop, or close the channel so the goroutine can exit its loop. This way, the goroutine does not wait forever and cleans up properly.
go
package main import ( "context" "fmt" "time" ) func worker(ctx context.Context, ch chan int) { for { select { case val, ok := <-ch: if !ok { fmt.Println("Channel closed, worker exiting") return } fmt.Println("Received:", val) case <-ctx.Done(): fmt.Println("Context cancelled, worker exiting") return } } } func main() { ctx, cancel := context.WithCancel(context.Background()) ch := make(chan int) go worker(ctx, ch) ch <- 1 ch <- 2 cancel() // signal worker to stop close(ch) // close channel to unblock time.Sleep(500 * time.Millisecond) fmt.Println("Main done") }
Output
Received: 1
Received: 2
Context cancelled, worker exiting
Main done
Prevention
To avoid goroutine leaks, always:
- Use
context.Contextto manage goroutine lifetimes. - Close channels when no more data will be sent.
- Avoid blocking operations without timeouts or exit conditions.
- Use tools like
go vetor linters to detect leaks. - Design goroutines to listen for cancellation signals.
Related Errors
Similar issues include:
- Deadlocks: When goroutines wait on each other forever.
- Channel leaks: When channels are not closed and goroutines wait indefinitely.
- Resource leaks: When goroutines hold resources like files or network connections without releasing.
Fixes usually involve proper synchronization, closing channels, and using context cancellation.
Key Takeaways
Always use context cancellation or channel closing to stop goroutines cleanly.
Avoid blocking operations without exit conditions inside goroutines.
Use linters and tools to detect goroutine leaks early.
Design goroutines to listen for stop signals to prevent leaks.
Closing channels signals goroutines to exit their loops safely.