A sync.WaitGroup lets one goroutine wait for a collection of goroutines to finish. Register work with Add, launch goroutines, defer Done in each goroutine, and call Wait as the synchronization point. section 7.2.4
Coordinate concurrent cooking tasks
Create a pointer to a WaitGroup, call Add(2), launch the cooking functions, and wait for both to report completion. Pass the pointer rather than a copy; otherwise Done updates a different WaitGroup and Wait can panic. section 7.2.4
Listing 7.7 Cooking example using sync.WaitGroup
package main
import (
"fmt"
"sync"
)
func main() {
wg := &sync.WaitGroup{} #1
wg.Add(2) #2
go cookRice(wg) #3
go cookCurry(wg) #3
wg.Wait() #4
}
func cookRice(wg *sync.WaitGroup) {
defer wg.Done() #5
fmt.Println("Cooking rice...")
// prepare rice
}
func cookCurry(wg *sync.WaitGroup) {
defer wg.Done() #5
fmt.Println("Preparing curry sauce...")
// prepare curry
}
Runnable cooking example showing Add before goroutine launch, deferred Done, and Wait after starting both goroutines. section 7.2.4
Use the core operations in this order:
- Call Add with the number of goroutines being tracked; call Add repeatedly when work is discovered in a loop. section 7.2.4
- Defer Done at the start of every tracked goroutine so completion is reported even when the function returns early. section 7.2.4
- Call Wait only after Add and goroutine launch, then continue when the corresponding Done calls have completed. section 7.2.4
- Keep tracking near the coordinator that knows how many goroutines it started; defer Wait when a listener must not return early. section 9.5.1
- Use errgroup.Group instead when goroutine errors need handling; use WaitGroup when errors require no specific treatment. section 7.6
Parallel cache test
A test can register ten tasks, start one goroutine per task, defer Done in each closure, and wait before the test returns. Running concurrent writes can expose cache races when combined with race testing. section 7.3.1 section 7.6




