1

In the below code:

package main

import "fmt"

func main() {
    for i := 0; i <= 9; i++ {
        go func() {
            fmt.Println(i)
        }()
    }
}

Output:

code$ 
code$ go install github.com/myhub/cs61a
code$ bin/cs61a 
code$ 

Above program does not provide any output.

1) Is their a data race for single memory location i among 10 go-routines?

2) Why above code does not print value of free variable i?

icza
  • 389,944
  • 63
  • 907
  • 827
overexchange
  • 15,768
  • 30
  • 152
  • 347
  • This is also covered in the FAQ: https://golang.org/doc/faq#closures_and_goroutines – JimB May 07 '20 at 13:27

1 Answers1

10

Is there a data race?

Yes, confirm it by running go run -race example.go. The main goroutine writes i, and the other goroutines read it without any synchronization. See Passing parameters to function closure; Why do these two for loop variations give me different behavior? and Register multiple routes using range for loop slices/map.

Why above code does not print anything?

Because when the main goroutine ends, your program ends as well. It does not wait for other non-main goroutines to finish. See No output from goroutine

Fix

Make a copy of the loop variable, and use that in the closures, and use a sync.WaitGroup to wait for the launched goroutines to end:

var wg sync.WaitGroup
for i := 0; i <= 9; i++ {
    i2 := i
    wg.Add(1)
    go func() {
        defer wg.Done()
        fmt.Println(i2)
    }()
}
wg.Wait()

This will output (try it on the Go Playground):

9
0
2
1
6
5
3
7
8
4

An alternative is to pass i as a parameter to the launched function:

var wg sync.WaitGroup
for i := 0; i <= 9; i++ {
    wg.Add(1)
    go func(i int) {
        defer wg.Done()
        fmt.Println(i)
    }(i)
}
wg.Wait()

Try this one on the Go Playground.

icza
  • 389,944
  • 63
  • 907
  • 827
  • How `Add` & `Done` access `wg` to increment & decrement in an atomic way? from each go-routine – overexchange May 06 '20 at 17:28
  • 1
    @overexchange Check their sources. They use the `sync/atomic` package among other things. – icza May 06 '20 at 17:30
  • What happens, if I say `runtime.Gosched()` in `main()` after the for-loop statement? instead of using `wg` – overexchange May 06 '20 at 20:14
  • @overexchange Nothing. `runtime.Gosched()` is not a synchronization tool, it (may) just help the scheduler. Without proper synchronization, you have no guarantee about goroutine execution order. – icza May 06 '20 at 21:18