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

时间:2016-08-29 13:33:29

标签: multithreading go concurrency goroutine

我正在学习Go并尝试了解其并发功能。

我有以下程序。

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")
}

执行时我得到了:

4
0
1
3
2

这正是我想要的。但是,如果我稍作修改:

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")
}

我得到的将是:

5
5
5
5
5

我不太了解这种差异。任何人都可以帮助解释这里发生的事情以及Go运行时如何执行此代码?

3 个答案:

答案 0 :(得分:3)

一般规则是,不要在goroutines之间共享数据。在第一个示例中,您基本上为每个goroutine提供了他们自己的x副本,并且它们以它们到达print语句的任何顺序打印出来。在第二个示例中,它们都引用相同的循环变量,并且在它们中的任何一个打印时递增到5。我不相信那里的输出是有保证的,只是碰巧创建goroutines的循环比goroutines自己到达打印部分的速度要快。

答案 1 :(得分:3)

每次x := i的运行都有新变量,
通过在goroutine中打印x的地址,此代码可以很好地显示差异:
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")
}

输出:

0xc0420301e0
0xc042030200
0xc0420301e8
0xc0420301f0
0xc0420301f8
Done

使用go build -race构建您的第二个示例并运行它:
您会看到:WARNING: DATA RACE

这样会很好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")
}

输出:

0
4
1
2
3
Done

答案 2 :(得分:0)

用简单的英语解释有点困难,但我会尽力而为。

你看,每次生成一个新的 goroutine 时,都会有一个初始化时间,不管它有多小,它总是存在的。因此,在您的第二种情况下,在任何 goroutine 开始之前,整个循环已经完成了 5 次变量的递增。当 goroutines 完成初始化时,他们看到的只是最终的变量值 5。

不过,在您的第一种情况下, x 变量保留了 i 变量的副本,以便在 goroutine 启动时,将 x get 传递给它们。请记住,这里增加的是 i,而不是 xx 是固定的。所以,当 goroutine 启动时,它们得到一个固定值。