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
TA贡献1824条经验 获得超6个赞
一般规则是,不要在 goroutine 之间共享数据。在第一个示例中,您基本上为每个 goroutine 提供了自己的 副本x
,然后他们按照到达 print 语句的任何顺序将其打印出来。在第二个示例中,它们都引用了相同的循环变量,并且在它们中的任何一个打印它时递增到 5。我不相信那里的输出是有保证的,只是碰巧创建 goroutine 的循环完成得比 goroutine 本身到达打印部分的速度更快。
TA贡献1831条经验 获得超10个赞
用简单的英语解释有点困难,但我会尽力而为。
你看,每次你生成一个新的 goroutine 时,都有一个初始化时间,不管它多么微不足道,它总是在那里。所以,在你的第二种情况下,整个循环在任何 goroutine 开始之前就已经完成了变量的 5 次递增。当 goroutine 完成初始化时,他们看到的只是最终的变量值,即 5。
但是,在您的第一种情况下, x 变量保留了 i 变量的副本,以便当 goroutine 启动时, x get 传递给它们。请记住,这里是i
递增的,而不是x
. x
是固定的。因此,当 goroutine 启动时,它们会得到一个固定值。
- 3 回答
- 0 关注
- 273 浏览
添加回答
举报