Iteration variables and closures

yourbasic.org/golang

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.

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 for a detailed epxlanation of data races in Go.

Share this page: