为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

Go
汪汪一只猫 2022-03-07 15:50:36
我正在学习 Go 并试图了解它的并发特性。我有以下程序。package mainimport (    "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")}执行时我得到:40132这正是我想要的。但是,如果我对其稍作修改:package mainimport (    "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")}我得到的是:55555我不太明白其中的区别。谁能帮助解释这里发生了什么以及 Go 运行时如何执行此代码?
查看完整描述

3 回答

?
繁星淼淼

TA贡献1775条经验 获得超11个赞

每次运行都有新变量x := i,

这段代码通过打印xgoroutine 内部的地址很好地显示了差异:

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


这会很好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


查看完整回答
反对 回复 2022-03-07
?
慕妹3242003

TA贡献1824条经验 获得超6个赞

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


查看完整回答
反对 回复 2022-03-07
?
慕哥6287543

TA贡献1831条经验 获得超10个赞

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

你看,每次你生成一个新的 goroutine 时,都有一个初始化时间,不管它多么微不足道,它总是在那里。所以,在你的第二种情况下,整个循环在任何 goroutine 开始之前就已经完成了变量的 5 次递增。当 goroutine 完成初始化时,他们看到的只是最终的变量值,即 5。

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


查看完整回答
反对 回复 2022-03-07
  • 3 回答
  • 0 关注
  • 273 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信