Golang / Golang Internals and Memory Management Interview Questions
How does sync.WaitGroup work and what are common mistakes?
sync.WaitGroup lets one goroutine wait for a collection of goroutines to finish. The three methods — Add(n), Done(), and Wait() — implement a simple counting semaphore.
var wg sync.WaitGroup // CORRECT: Add BEFORE launching the goroutine for i := 0; i < 5; i++ { wg.Add(1) // increment BEFORE go statement go func(id int) { defer wg.Done() // always use defer â ensures Done even on panic fmt.Println("worker", id) }(i) } wg.Wait() // blocks until count reaches zero // WRONG: Add inside the goroutine â race condition for i := 0; i < 5; i++ { go func(id int) { wg.Add(1) // may execute AFTER Wait() returns â data race! defer wg.Done() fmt.Println(id) }(i) } wg.Wait() // might return before all goroutines call Add // WRONG: reusing WaitGroup before Wait returns // Do not call Add on a WaitGroup whose Wait has not yet returned // Pattern: limit concurrency with a semaphore channel sem := make(chan struct{}, 10) // max 10 goroutines at once for _, item := range items { wg.Add(1) sem <- struct{}{} // acquire slot go func(it Item) { defer func() { <-sem; wg.Done() }() process(it) }(item) } wg.Wait()
A WaitGroup must not be copied after first use — embedding a WaitGroup in a struct and passing the struct by value silently copies the counter state. Always pass pointers to structs containing a WaitGroup, or pass the WaitGroup itself by pointer.
More Related questions...