How does Golang share variables between goroutines?

user130268 picture user130268 · Aug 29, 2016 · Viewed 8.9k times · Source

I'm learning Go and trying to understand its concurrency features.

I have the following program.

package main

import (
    "fmt"
    "sync"
)

func main() {
    var wg sync.WaitGroup

    for i := 0; i < 5; i++ {
        wg.Add(1)

        x := i

        go func() {
            defer wg.Done()
            fmt.Println(x)
        }()

    }

    wg.Wait()
    fmt.Println("Done")
}

When executed I got:

4
0
1
3
2

It's just what I want. However, if I make slight modification to it:

package main

import (
    "fmt"
    "sync"
)

func main() {
    var wg sync.WaitGroup

    for i := 0; i < 5; i++ {
        wg.Add(1)

        go func() {
            defer wg.Done()
            fmt.Println(i)
        }()

    }

    wg.Wait()
    fmt.Println("Done")
}

What I got will be:

5
5
5
5
5

I don't quite understand the difference. Can anyone help to explain what happened here and how Go runtime execute this code?

Answer

user6169399 picture user6169399 · Aug 29, 2016

You have new variable on each run of x := i,
This code shows difference well, by printing the address of x inside goroutine:
The Go Playground:

package main

import (
    "fmt"
    "sync"
)

func main() {
    var wg sync.WaitGroup
    for i := 0; i < 5; i++ {
        wg.Add(1)
        x := i
        go func() {
            defer wg.Done()
            fmt.Println(&x)
        }()
    }
    wg.Wait()
    fmt.Println("Done")
}

output:

0xc0420301e0
0xc042030200
0xc0420301e8
0xc0420301f0
0xc0420301f8
Done

And build your second example with go build -race and run it:
You will see: WARNING: DATA RACE


And this will be fine The Go Playground:

//go build -race
package main

import (
    "fmt"
    "sync"
)

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

output:

0
4
1
2
3
Done