为什么 goroutines 在 for 循环终止之前不执行?



像你们大多数人一样,我熟悉 Go 在 for 循环中重用迭代器变量的事实,因此每个 goroutine 的闭包将捕获相同的变量。一个典型的例子是以下代码,它将始终从每个goroutine打印循环的最终值:

func main() {
for i := 0; i < 5; i++ {
go func() {
fmt.Println(i) // prints 5 each time
}()
}
time.Sleep(100 * time.Millisecond)
}

我无法找到解释的是,为什么goroutines直到循环完成后才会执行。甚至下面的代码也会生成ii的值,作为调用 goroutine设置的10

func main() {
for i := 0; i < 5; i++ {
ii := i
go func() {
fmt.Println(ii) // prints 10 each time...!
}()
ii = 10
}
time.Sleep(100 * time.Millisecond)
}

我读过的最接近解释的是,for-loops 通常比 goroutines 执行得更快。这给我提出了两个问题:1.这是真的吗?2. 为什么?

在处理多个 goroutine 时,永远不要假设执行顺序 - 无论它多么诱人。不要睡觉;不要调用运行时。戈舍德;不要假设任何事情

确保执行顺序的唯一保证方法是通过同步方法,如通道、sync.Mutexsync.WaitGroups等。

一切皆有可能,因为该程序具有数据竞赛。

撇开数据争用不谈,没有证据表明goroutines在for循环完成后执行。值 10 是在 goroutine 启动后分配给语句中的ii,而不是在 for 循环之后。

内存模型允许编译器在 for 循环体开始时将两个ii赋值优化为单个赋值。可能是 goroutines 立即执行,但 goroutines 从优化中看到值 10。

你应该像这样使用 goroutine:

package main
import "fmt"
import "time"
func main() {
for i := 0; i < 5; i++ {
ii := i
go func(k int) {
fmt.Println(k) // prints 10 each time...!
}(ii)
ii = 10
}
time.Sleep(100 * time.Millisecond)
}

最新更新