Go Concurrency Deep Dives


Part 1: Goroutines & WaitGroups — Foundation

The Problem You Hit

Your code deadlocked because of a subtle math error in the loop:

for i := 1; i <= 5; i++ {
    wg.Add(i)  // ❌ Adds 1, then 2, then 3, then 4, then 5 = 15 total
    go worker(i, &wg)  // But only spawns 5 goroutines
}

Counter state: 1 + 2 + 3 + 4 + 5 = 15

Done() calls: 5 workers × 1 = 5

Result: Counter sits at 10. Wait() blocks forever. Deadlock.

The fix:

for i := 1; i <= 5; i++ {
    wg.Add(1)  // ✅ Increment by 1 each time
    go worker(i, &wg)
}

Now the counter is 5, and 5 workers call Done() → counter reaches 0 → Wait() unblocks.


WaitGroup Mental Model

Think of WaitGroup as a semaphore counter with three operations:

Operation Effect When to Use
Add(n) Increment counter by n Before spawning goroutines
Done() Decrement by 1 (alias for Add(-1)) When goroutine finishes
Wait() Block until counter = 0 In main or coordinating thread

Key invariant: Counter must never go negative, or panic.


Four Cardinal Rules

1. Call Add() before go