concurrency
2w ago

Common Pitfalls and Mistakes

8 views • 0 upvotes

1. Goroutine Leaks

❌ Problem: Goroutine Never Exits
go
func 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
go
func 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:
go
func 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:
go
func 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:
go
func 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:
go
var 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
go
var (
    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:
bash
go run -race main.go

5. Deadlocks

❌ Classic Deadlock:
go
func main() {
    ch := make(chan int)
    ch <- 1 // Blocks forever - no receiver!
    fmt.Println(<-ch)
}
// Output: fatal error: all goroutines are asleep - deadlock!
✅ Solution:
go
func main() {
    ch := make(chan int, 1) // Buffered channel
    ch <- 1 // Doesn't block
    fmt.Println(<-ch)
}

6. Forgetting to Close Channels

❌ Problem:
go
func 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:
go
func 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?

Difficulty & Status

easy
Lvl. 2
Community Verified
Progress: 11%
Answered by: shubham vyasPrev TopicNext Topic