为什么 goroutine 在 for 循环结束之前不执行?

huangapple go评论103阅读模式
英文:

Why do goroutines not execute until the for-loop has terminated?

问题

大多数人都知道,在for循环中,Go语言会重用迭代变量,因此每个goroutine都会捕获相同的变量。下面是一个典型的例子,它会始终打印每个goroutine中循环的最终值:

func main() {
    for i := 0; i < 5; i++ {
        go func() {
            fmt.Println(i) // 每次都打印5
        }()
    }
    time.Sleep(100 * time.Millisecond)
}

但是,我找不到关于为什么goroutine直到循环完成后才执行的解释。即使下面的代码在调用goroutine之后将ii的值设置为10,它仍然会打印出10

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

我读到的最接近解释的是,for循环通常比goroutine执行得更快。这给我带来了两个问题:1. 这是真的吗?2. 为什么会这样?

英文:

Like most of you, I'm familiar with the fact that Go reuses the iterator variable in a for-loop, so closures for each goroutine will capture the same variable. A typical example of this the following code which will always print the final value of the loop from each goroutine:

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

What I haven't been able to find an explanation of, is why the goroutines do not execute until after the loop is completed. Even the following code produces the value of ii as 10 which is set after the goroutine is called:

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

The closest thing to an explanation I've read is that for-loops typically execute faster than goroutines. This raises two questions for me: 1. Is that true? 2. Why?

答案1

得分: 2

永远不要假设在处理多个goroutine时的执行顺序-无论多么诱人。不要使用sleep;不要调用runtime.Gosched;不要假设任何事情

确保执行顺序的唯一可靠方法是使用同步方法,如通道、sync.Mutexsync.WaitGroup等。

英文:

Never assume the order of execution when dealing with more than one goroutine - no matter how tempting it may be. Don't put in sleeps; don't call runtime.Gosched; don't assume anything.

The only guaranteed way to ensure order of execution is via synchronization methods such as channels, sync.Mutex, sync.WaitGroups etc.

答案2

得分: 2

任何事情都有可能,因为程序存在数据竞争。

暂且不考虑数据竞争,没有证据表明goroutine在for循环完成后执行。值10被赋给ii是在启动goroutine之后的语句中,而不是在for循环之后。

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

英文:

Anything is possible because the program has a data race.

Setting aside the data race, there's no evidence that the goroutines execute after the for loop is completed. The value 10 is assigned to ii in the statement after the goroutine is started, not after the for loop.

The memory model allows for the compiler to optimize the two ii assignments to a single assignment at the start of the for loop body. It could be that the goroutines execute immediately, but the goroutines see the value of 10 from the optimization.

答案3

得分: 0

你应该像这样使用goroutine:

package main

import "fmt"
import "time"

func main() {
    for i := 0; i < 5; i++ {
        ii := i
        go func(k int) {
            fmt.Println(k) // 每次都打印10...!
        }(ii)
        ii = 10
    }
    time.Sleep(100 * time.Millisecond)
}
英文:

you should use the goroutine like this:

package main

import &quot;fmt&quot;
import &quot;time&quot;

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

huangapple
  • 本文由 发表于 2021年10月12日 09:29:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/69534108.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定