How to Use sync.WaitGroup in Go for Goroutine Synchronization
In Go, use
sync.WaitGroup to wait for a collection of goroutines to finish. Call Add(n) to set the number of goroutines, then each goroutine calls Done() when finished, and the main goroutine calls Wait() to block until all are done.Syntax
The sync.WaitGroup type helps you wait for multiple goroutines to finish. You use three main methods:
Add(n int): Set how many goroutines to wait for.Done(): Call this inside each goroutine when it finishes.Wait(): Call this to block until all goroutines have calledDone().
go
var wg sync.WaitGroup wg.Add(n) // n is number of goroutines for i := 0; i < n; i++ { go func() { defer wg.Done() // goroutine work here }() } wg.Wait() // wait for all goroutines to finish
Example
This example shows how to launch 3 goroutines that print messages and how sync.WaitGroup waits for all to finish before the program ends.
go
package main import ( "fmt" "sync" "time" ) func main() { var wg sync.WaitGroup wg.Add(3) // we will wait for 3 goroutines for i := 1; i <= 3; i++ { go func(id int) { defer wg.Done() // mark this goroutine done when finished time.Sleep(time.Duration(id) * 500 * time.Millisecond) // simulate work fmt.Printf("Goroutine %d finished\n", id) }(i) } wg.Wait() // wait for all goroutines fmt.Println("All goroutines completed") }
Output
Goroutine 1 finished
Goroutine 2 finished
Goroutine 3 finished
All goroutines completed
Common Pitfalls
Common mistakes when using sync.WaitGroup include:
- Calling
Done()fewer times thanAdd()causesWait()to block forever. - Calling
Add()after starting goroutines can cause race conditions. - Not using
defer wg.Done()inside goroutines can lead to missed signals if the goroutine panics or returns early.
go
package main import ( "fmt" "sync" ) func main() { var wg sync.WaitGroup wg.Add(1) go func() { // Forgot to call Done() fmt.Println("Goroutine running but Done not called") }() // This will block forever because Done() was never called // wg.Wait() // Correct way: // wg.Add(1) // go func() { // defer wg.Done() // fmt.Println("Goroutine running and Done called") // }() // wg.Wait() }
Output
Goroutine running but Done not called
Quick Reference
Remember these tips when using sync.WaitGroup:
- Always call
Add()before starting goroutines. - Use
defer wg.Done()at the start of each goroutine. - Call
Wait()to block until all goroutines finish. - Never call
Wait()beforeAdd()completes.
Key Takeaways
Use sync.WaitGroup to wait for multiple goroutines to finish safely.
Call Add(n) before starting goroutines to set the count.
Each goroutine must call Done() exactly once to signal completion.
Call Wait() to block until all goroutines have finished.
Use defer wg.Done() inside goroutines to avoid missing Done calls.