1. Goroutine Leaks
❌ Problem: Goroutine Never Exits
gofunc leak() { ch := make(chan int) go func() { val := <-ch // Blocks forever! fmt.Println(val) }() // Channel is never written to // Goroutine leaks! }
✅ Solution: Use Context or Timeout
gofunc noLeak() { ch := make(chan int) ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) defer cancel() go func() { select { case val := <-ch: fmt.Println(val) case <-ctx.Done(): fmt.Println("Timeout, exiting") } }() }
2. Closing Channels from Receiver
❌ Wrong:
gofunc wrong() { ch := make(chan int) go func() { for val := range ch { fmt.Println(val) } }() ch <- 1 ch <- 2 close(ch) // Sender closes - CORRECT } func alsoWrong() { ch := make(chan int) go func() { for val := range ch { fmt.Println(val) close(ch) // ❌ Receiver closes - WRONG! } }() ch <- 1 // May panic! }
Rule: Only the sender should close channels.
3. Forgetting WaitGroup.Wait()
❌ Problem:
gofunc main() { var wg sync.WaitGroup for i := 0; i < 5; i++ { wg.Add(1) go func(n int) { defer wg.Done() fmt.Println(n) }(i) } // Forgot wg.Wait()! // Program exits before goroutines finish }
✅ Solution:
gofunc main() { var wg sync.WaitGroup for i := 0; i < 5; i++ { wg.Add(1) go func(n int) { defer wg.Done() fmt.Println(n) }(i) } wg.Wait() // Wait for all goroutines }
4. Race Conditions
❌ Problem:
govar counter int func increment() { counter++ // Race condition! } func main() { for i := 0; i < 1000; i++ { go increment() } time.Sleep(time.Second) fmt.Println(counter) // Unpredictable result }
✅ Solution: Use Mutex or Atomic
govar ( counter int mu sync.Mutex ) func increment() { mu.Lock() counter++ mu.Unlock() } // OR use atomic var counter int64 func increment() { atomic.AddInt64(&counter, 1) }
Detect with Race Detector:
bashgo run -race main.go
5. Deadlocks
❌ Classic Deadlock:
gofunc main() { ch := make(chan int) ch <- 1 // Blocks forever - no receiver! fmt.Println(<-ch) } // Output: fatal error: all goroutines are asleep - deadlock!
✅ Solution:
gofunc main() { ch := make(chan int, 1) // Buffered channel ch <- 1 // Doesn't block fmt.Println(<-ch) }
6. Forgetting to Close Channels
❌ Problem:
gofunc generate() <-chan int { ch := make(chan int) go func() { for i := 0; i < 10; i++ { ch <- i } // Forgot to close(ch)! }() return ch } func main() { for n := range generate() { fmt.Println(n) } // Blocks forever after 10 items! }
✅ Solution:
gofunc generate() <-chan int { ch := make(chan int) go func() { defer close(ch) // Always close when done for i := 0; i < 10; i++ { ch <- i } }() return ch }
Was this helpful?