Race Conditions
In this page:
What a Race Condition Looks Like
When two or more goroutines read and write the same variable without any coordination, the final result depends on unpredictable timing -- the same program can produce different, sometimes wrong, results across separate runs.
Example: What a Race Condition Looks Like
package main
import (
"fmt"
"sync"
)
func main() {
total := 0
var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
total++ // unsynchronized: a real race in production code
}()
}
wg.Wait()
fmt.Println("total (may vary if truly racy):", total)
}
Login to try C/C++/Java/PHP code in the editor
Detecting Races with -race
Go ships a race detector, enabled with 'go run -race' or 'go test -race', which instruments memory accesses and reports the exact goroutines and lines involved when it observes a genuine race, even if the output happened to look correct that run.
Note: Always run tests with -race in CI when the code involves goroutines sharing data.
Example: Detecting Races with -race
go run -race main.go
⚠️ Run this command in your terminal.
Fixing a Race with a Mutex
The standard fix for a race on shared data is to protect every access to it with a mutex, ensuring only one goroutine can read or write the variable at any given instant.
Example: Fixing a Race with a Mutex
package main
import (
"fmt"
"sync"
)
func main() {
var mu sync.Mutex
total := 0
var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
mu.Lock()
total++
mu.Unlock()
}()
}
wg.Wait()
fmt.Println("total:", total)
}
Login to try C/C++/Java/PHP code in the editor
- Assuming a program that 'usually works' during testing has no race condition -- races are timing-dependent and can hide until production load exposes them.
- Reading a shared variable from one goroutine while another writes it, without a mutex or channel, believing simple reads are always safe.
- Never running 'go test -race' or 'go run -race', missing races that only the race detector reliably surfaces.
- A race condition occurs when multiple goroutines access shared data concurrently and at least one writes it, without synchronization.
- Races produce unpredictable results that can vary between runs, making them hard to debug by inspection alone.
- Go's built-in race detector (-race flag) instruments a program to catch races at runtime.
- Fixing a race means synchronizing access with a mutex, channel, or avoiding the shared mutable state entirely.
Chapter Quiz — Complete all 8 topics to unlock
0/8 topics done
Complete these topics first: