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

为什么这个程序中存在竞争条件?

为什么这个程序中存在竞争条件?

Go
冉冉说 2021-06-10 14:39:35
我正在查看Golang文档中的典型数据竞争,我不太明白为什么这个程序会出现问题:func main() {    var wg sync.WaitGroup    wg.Add(5)    for i := 0; i < 5; i++ {        go func() {            fmt.Println(i) // Not the 'i' you are looking for.            wg.Done()        }()    }    wg.Wait()}它5, 5, 5, 5, 5会在我希望它打印时打印0, 1, 2, 3, 4(不一定按此顺序)。在我看来,当 goroutine 在循环内创建时, 的值i是已知的(例如,可以log.Println(i)在循环开始时执行 a并查看预期值)。所以我希望 goroutinei在它被创建时捕获它的值并在以后使用它。显然这不是正在发生的事情,但为什么呢?
查看完整描述

3 回答

?
烙印99

TA贡献1829条经验 获得超13个赞

您的函数文字i从外部范围引用。如果您请求 的值i,您将获得当前的任何值i。为了使用iGo 例程创建时的值,请提供一个参数:


func main() {

    var wg sync.WaitGroup

    wg.Add(5)

    for i := 0; i < 5; i++ {

        go func(i int) {

            fmt.Println(i)

            wg.Done()

        }(i)

    }

    wg.Wait()

}


查看完整回答
反对 回复 2021-06-21
?
慕莱坞森

TA贡献1810条经验 获得超4个赞

该变量i未在函数字面量中声明,因此它成为闭包的一部分。理解闭包的一个简单方法是考虑如何实现它们。简单的解决方案是使用指针。你可以认为函数字面量被编译器改写成一些


func f123(i *int) {

        fmt.Println(*i)

        wg.Done            

}

  • 在调用此函数时,通过 go 语句,将i变量的地址传递给被调用的 f123(编译器生成的示例名称)。

  • 您可能正在使用默认的 GOMAXPROCS==1,因此 for 循环在没有任何调度的情况下执行 5 次,因为该循环没有 I/O 或其他“调度点”,例如通道操作。

  • 当循环以 结束时i == 5wg.Waitfinally 会触发五个准备运行的 goroutines(对于 f123)的执行。他们当然都有指向同一个整数变量的相同指针i

  • 现在每个 goroutine 都看到相同的i值 5。

当 GOMAXPROCS > 1 运行时,或者当循环产生控制时,您可能会得到不同的输出。这也可以通过例如runtime.Gosched来完成。



查看完整回答
反对 回复 2021-06-21
  • 3 回答
  • 0 关注
  • 189 浏览
慕课专栏
更多

添加回答

举报

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