Does this counter give the right answer with GOMAXPROCS=1? What about counter++ in general?
Question 235MediumGo 1.22 to 1.25
func main() {
runtime.GOMAXPROCS(1)
var n int
var wg sync.WaitGroup
for range 1000 {
wg.Go(func() { n++ })
}
wg.Wait()
fmt.Println(n)
}
It will often print 1000 with one P, but that is not guaranteed and the program is still a data race that -race flags. The reasons:
n++is a load, an add and a store. The runtime can preempt a goroutine between any of them; asynchronous preemption has existed since Go 1.14.- The memory model speaks about happens-before, not about how many CPUs you have. With no synchronization the compiler may keep
nin a register. - The race detector reports it no matter what
GOMAXPROCSis.
Correct versions:
var n atomic.Int64
wg.Go(func() { n.Add(1) })
// or confine the state to one goroutine and send results over a channel,
// or protect n with a sync.Mutex.
What the interviewer is looking for: correctness must never depend on scheduler details. GOMAXPROCS=1 hides races; it does not remove them.
More on Concurrency Patterns & sync
- Q233What does this program print?
- Q234How does the race detector work, and what are its limitations? Is a "benign" data race ever OK?
- 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.
- Q238Implement fan-out / fan-in: a generic Merge that combines N channels into one.
- Q239How do nil channels behave in select, and how are they used to merge two channels?