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

我正在查看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在它被创建时捕获它的值并在以后使用它。


显然这不是正在发生的事情,但为什么呢?


冉冉说
浏览 190回答 3
3回答

烙印99

您的函数文字i从外部范围引用。如果您请求 的值i,您将获得当前的任何值i。为了使用iGo 例程创建时的值,请提供一个参数:func main() {&nbsp; &nbsp; var wg sync.WaitGroup&nbsp; &nbsp; wg.Add(5)&nbsp; &nbsp; for i := 0; i < 5; i++ {&nbsp; &nbsp; &nbsp; &nbsp; go func(i int) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Println(i)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; wg.Done()&nbsp; &nbsp; &nbsp; &nbsp; }(i)&nbsp; &nbsp; }&nbsp; &nbsp; wg.Wait()}

慕莱坞森

该变量i未在函数字面量中声明,因此它成为闭包的一部分。理解闭包的一个简单方法是考虑如何实现它们。简单的解决方案是使用指针。你可以认为函数字面量被编译器改写成一些func f123(i *int) {&nbsp; &nbsp; &nbsp; &nbsp; fmt.Println(*i)&nbsp; &nbsp; &nbsp; &nbsp; wg.Done&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;}在调用此函数时,通过 go 语句,将i变量的地址传递给被调用的 f123(编译器生成的示例名称)。您可能正在使用默认的 GOMAXPROCS==1,因此 for 循环在没有任何调度的情况下执行 5 次,因为该循环没有 I/O 或其他“调度点”,例如通道操作。当循环以 结束时i == 5,wg.Waitfinally 会触发五个准备运行的 goroutines(对于 f123)的执行。他们当然都有指向同一个整数变量的相同指针i。现在每个 goroutine 都看到相同的i值 5。当 GOMAXPROCS > 1 运行时,或者当循环产生控制时,您可能会得到不同的输出。这也可以通过例如runtime.Gosched来完成。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go