Go gotcha: Iteration variables and closures
Why does this program…
func main() {
var wg sync.WaitGroup
wg.Add(5)
for i := 0; i < 5; i++ {
go func() {
fmt.Print(i)
wg.Done()
}()
}
wg.Wait()
fmt.Println()
}
…print…
55555
(A WaitGroup waits for a collection of goroutines to finish.)
Answer
There is a data race: the variable i
is shared by six (6) goroutines.
A data race occurs when two goroutines access the same variable concurrently and at least one of the accesses is a write.
(See Data races explained for a detailed discussion.)
To avoid this, use a local variable and pass the number as a parameter when starting the goroutine:
func main() {
var wg sync.WaitGroup
wg.Add(5)
for i := 0; i < 5; i++ {
go func(n int) { // Use a local variable.
fmt.Print(n)
wg.Done()
}(i)
}
wg.Wait()
fmt.Println()
}
Example output:
40123
It’s also possible to avoid this data race while still using a closure, but then we must take care to use a unique variable for each goroutine:
func main() {
var wg sync.WaitGroup
wg.Add(5)
for i := 0; i < 5; i++ {
n := i // Create a unique variable for each closure.
go func() {
fmt.Print(n)
wg.Done()
}()
}
wg.Wait()
fmt.Println()
}
See Data races explained for a detailed epxlanation of data races in Go.
Comments
Be the first to comment!