What is a WaitGroup in Go and how does it coordinate goroutines?
Learn how sync.WaitGroup coordinates goroutines in Go using Add, Done, and Wait, with best practices, common mistakes, and a clear code example.
Expected Interview Answer
A sync.WaitGroup is a Go concurrency primitive that lets one goroutine wait until a collection of other goroutines has finished, using a counter of outstanding work.
You call Add(n) to register how many goroutines you're waiting on, each goroutine calls Done() (usually via defer) when it completes to decrement the counter, and Wait() blocks until the counter reaches zero. The critical rule is that Add must be called before launching the goroutine (not inside it), otherwise Wait may return before all work is registered. A WaitGroup coordinates completion but does not collect return values — for that you combine it with channels or shared state guarded by a mutex.
- Simple, lock-free way to wait for many goroutines to finish
- No need to manually count completions with channels
- defer wg.Done() makes cleanup robust even on early returns or panics
- Zero value is ready to use with no initialization
- Composes well with channels for gathering results
AI Mentor Explanation
Think of a captain who won't declare the innings until every assigned fielder has jogged back from the boundary. The captain notes how many players went out (Add), each raises a hand as they return (Done), and only when the last hand goes up does the captain call the huddle (Wait). No player is left stranded on the field before the next phase begins.
Step-by-Step Explanation
Step 1
Declare the WaitGroup
Create a var wg sync.WaitGroup; its zero value is ready to use, no initialization needed.
Step 2
Register work
Call wg.Add(n) before launching goroutines to set the counter to the number you'll wait on.
Step 3
Signal completion
Inside each goroutine, call defer wg.Done() so the counter decrements even on early return or panic.
Step 4
Wait for zero
In the coordinating goroutine, call wg.Wait() to block until the counter reaches zero.
Step 5
Gather results
If you need return values, combine the WaitGroup with a channel or a mutex-guarded slice.
What Interviewer Expects
- Knows the three methods: Add, Done, and Wait
- Explains that Add must be called before starting the goroutine
- Recommends defer wg.Done() for safety
- Understands WaitGroup coordinates completion, not result collection
- Aware that copying a WaitGroup after use is a bug
Common Mistakes
- Calling wg.Add inside the goroutine, causing Wait to return too early
- Forgetting to call Done, leaving Wait blocked forever
- Passing a WaitGroup by value instead of by pointer, copying its state
- Calling Done more times than Add, causing a negative counter panic
- Using a WaitGroup to pass return values instead of a channel
Best Answer (HR Friendly)
“A WaitGroup in Go is a simple tool that lets your program wait for several background tasks to finish before moving on. You tell it how many tasks to expect, each task reports when it's done, and the program pauses until all of them have completed.”
Code Example
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
urls := []string{"a", "b", "c"}
for _, u := range urls {
wg.Add(1) // register BEFORE launching
go func(name string) {
defer wg.Done() // decrement when finished
fmt.Println("processed", name)
}(u)
}
wg.Wait() // block until all goroutines call Done
fmt.Println("all done")
}Follow-up Questions
- Why must Add be called before the goroutine starts rather than inside it?
- How do you collect return values from goroutines coordinated by a WaitGroup?
- What happens if Done is called more times than Add?
- Why should a WaitGroup be passed by pointer, not by value?
- How does errgroup.Group extend WaitGroup with error handling?
MCQ Practice
1. Which method blocks until all goroutines finish?
Wait blocks the calling goroutine until the WaitGroup's internal counter reaches zero.
2. Where should wg.Add be called?
Add must run before the goroutine starts, or Wait may return before all work is registered, causing a race.
3. What happens if Done is called more times than Add?
Decrementing the counter below zero causes a runtime panic: 'sync: negative WaitGroup counter'.
Flash Cards
What are the three WaitGroup methods? — Add(n) to register work, Done() to signal completion, and Wait() to block until the counter hits zero.
Why use defer wg.Done()? — It guarantees the counter decrements even if the goroutine returns early or panics.
When must Add be called? — Before launching the goroutine, so Wait can't return before all work is registered.
Does WaitGroup return values? — No — it only coordinates completion; combine it with channels or a mutex to gather results.
Continue Learning
Related Interview Questions
What is a Mutex in Go and how does it differ from a channel for synchronization?
medium
What are channels in Go and how do they enable communication between goroutines?
medium
What is the difference between buffered and unbuffered channels in Go?
medium
What is the Go memory model and what does the happens-before relationship mean?
hard