A sync.WaitGroup lets code wait for a specified number of operations, usually goroutines, to finish. Its zero value is ready to use: Add increments an internal counter, Done decrements it, and Wait blocks until the counter reaches zero. A negative counter panics. section 9.11
A common mistake is calling Add inside the goroutine it is meant to track. The parent may reach Wait before that Add executes, observe a zero counter, and continue while work is still running. The result can be nondeterministic, and the race detector may report unsafe access. section 9.11
Register work before starting it
Problematic code calls wg.Add(1) inside each goroutine, so wg.Wait can return too early. Register all work first, or increment immediately before each go statement: wg.Add(3) for i := 0; i < 3; i++ { go func() { // work wg.Done() }() } wg.Wait() For a loop, the equivalent pattern is wg.Add(1) followed immediately by go func(...). Use the first form when the total is known; use the second when launching work incrementally. section 9.11
wg := sync.WaitGroup{}
var v uint64
for i := 0; i < 3; i++ {
go func() { #1
wg.Add(1) #2
atomic.AddUint64(&v, 1) #3
wg.Done() #4
}()
}
wg.Wait() #5
fmt.Println(v)
Trace how Add inside the goroutine allows Wait to unlock before all three operations register. section 9.11
Use WaitGroup for completion synchronization, not for result collection or error handling. For coordinated cancellation and error propagation, errgroup is identified as an alternative; channels and sync.Cond provide other synchronization patterns. section 9.15




