What does this program print?
Question 233HardGo 1.22 to 1.25
var done bool
var msg string
func setup() {
msg = "hello"
done = true
}
func main() {
go setup()
for !done {
}
fmt.Println(msg)
}
No output is guaranteed. This is a data race, and several things can happen:
- The compiler may hoist the load of
doneout of the loop, since nothing in the loop body can change it. The loop then never ends. - If the loop does exit,
mainmay still print an empty string. Nothing ordersmsg = "hello"before the read, so the writes can be observed out of order. go run -racereports the race.
The fix is to create a real happens-before edge:
var done atomic.Bool
var msg string
func setup() {
msg = "hello"
done.Store(true) // publishes msg
}
func main() {
go setup()
for !done.Load() {
runtime.Gosched()
}
fmt.Println(msg) // guaranteed "hello"
}
Better still, avoid spinning altogether: close a channel, or use a sync.WaitGroup. The atomic store and load publish msg because the write to msg happens-before Store, which happens-before the Load that observes it.
More on Concurrency Patterns & sync
- Q231Implement a lock-free "store max" using compare-and-swap. What is the ABA problem, and does it affect Go?
- Q232Explain the Go memory model and "happens-before". What synchronization edges does Go guarantee?
- Q234How does the race detector work, and what are its limitations? Is a "benign" data race ever OK?
- Q235Does this counter give the right answer with GOMAXPROCS=1? What about counter++ in general?
- Q236What does this print in Go 1.22+ vs before?
- Q237Implement a generic worker pool with a fixed number of workers, context cancellation, and no goroutine leaks.