doudanma9706
doudanma9706
2016-08-29 13:33
浏览 177
已采纳

Golang如何在goroutine之间共享变量?

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?

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 邀请回答

3条回答 默认 最新

  • doubu1964
    doubu1964 2016-08-29 13:45
    已采纳

    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
    
    点赞 评论
  • dongzouh51192
    dongzouh51192 2016-08-29 13:42

    The general rule is, don't share data between goroutines. In the first example, you essentially give each goroutine their own copy of x, and they print it out in whatever order they get to the print statement. In the second example, they all reference the same loop variable, and it is incremented to 5 by the time any of them print it. I don't believe the output there is guaranteed, it just happens that the loop creating goroutines finished faster than the goroutines themselves got to the printing part.

    点赞 评论
  • weixin_43031895
    weixin_43031895 2021-07-13 11:48

    defer Solve it

    点赞 评论

相关推荐