Why is calling wg.Add(1) inside each goroutine a bug, and where should it go?
This program launches 5 goroutines and waits for them with a WaitGroup, but it often prints nothing and exits before the goroutines run.
func main() {
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
go func() {
wg.Add(1)
defer wg.Done()
fmt.Println("work")
}()
}
wg.Wait()
}
Find and fix the bug.
wg.Add(1) runs inside the goroutine, but the scheduler may not start any goroutine before main reaches wg.Wait(). If Wait sees a zero counter it returns at once and the program can exit before the goroutines run. Fix: call wg.Add(1) in the loop before launching each goroutine.
- ✗Calling
wg.Add(1)inside the goroutine instead of before launching it - ✗Assuming the scheduler runs goroutines before
mainreacheswg.Wait() - ✗Thinking the bug is the deferred
Donerather than the lateAdd
- →Why does adding to the WaitGroup before
goestablish the needed happens-before withWait? - →What does the
Add/Waitdocumentation say about callingAddconcurrently?
Find the bug
func main() {
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
go func() {
wg.Add(1) // BUG: Add inside the goroutine
defer wg.Done()
fmt.Println("work")
}()
}
wg.Wait()
}
Why it is a bug
wg.Add(1) sits inside the goroutine. But go only schedules the launch — there is no guarantee any goroutine starts running before main reaches wg.Wait().
If the scheduler has not started the goroutines yet, the WaitGroup counter is still 0, so wg.Wait() returns immediately and main exits — the program can finish printing nothing. On top of that, Add and Wait running concurrently form a race on the counter.
✅ The fix is to call Add in the loop before launching the goroutine, while main is still guaranteed to be running alone:
for i := 0; i < 5; i++ {
wg.Add(1) // before go
go func() {
defer wg.Done()
fmt.Println("work")
}()
}
wg.Wait()