Go by Example: WaitGroups
Go 1.23
Coordinate concurrent tasks using WaitGroups. This example demonstrates how to use `sync.WaitGroup` to wait for a collection of goroutines to finish executing, a fundamental pattern for synchronization in Go programs.
Code
package main
import (
"fmt"
"sync"
"time"
)
// worker simulates a task that takes some time to complete
func worker(id int) {
fmt.Printf("Worker %d starting\n", id)
// Simulate expensive work
time.Sleep(time.Second)
fmt.Printf("Worker %d done\n", id)
}
func main() {
// WaitGroup is used to wait for all goroutines launched here to finish
var wg sync.WaitGroup
// Launch 5 worker goroutines
for i := 1; i <= 5; i++ {
// Increment the WaitGroup counter BEFORE starting the goroutine
wg.Add(1)
// Wrap the worker call in a closure to handle the WaitGroup
go func(id int) {
// Defer Done() to ensure it runs even if the worker panics
defer wg.Done()
worker(id)
}(i)
}
fmt.Println("Main: Waiting for workers to finish...")
// Block until the WaitGroup counter goes back to 0
wg.Wait()
fmt.Println("Main: All workers completed")
}Explanation
To wait for multiple goroutines to finish, we use a sync.WaitGroup. This is cleaner and more robust than using time.Sleep or managing multiple channels manually.
A WaitGroup maintains a counter. You increment it with Add() when starting a task, and decrement it with Done() when a task finishes. The Wait() method blocks execution until the counter reaches zero.
Important rules for WaitGroups:
- Always call Add() *before* starting the goroutine to avoid race conditions
- Use defer wg.Done() inside the goroutine to ensure it runs
- Pass WaitGroups by pointer if passing them to functions
Code Breakdown
21
Declaring a sync.WaitGroup. It doesn't need initialization; the zero value is ready to use.
26
Calling wg.Add(1) tells the WaitGroup we are starting 1 new task. This must happen in the main goroutine, not inside the new goroutine.
31
wg.Done() decrements the counter. Using defer ensures it runs when the function exits, preventing deadlocks if the code crashes.
39
wg.Wait() blocks the main function until the counter reaches zero (i.e., all 5 workers have called Done).

