问题
有以下三段代码,内容大体相同,只有很小的差别。
- 第一段
func main() {
runtime.GOMAXPROCS(0)
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go func(n int) {
print(n, " ")
wg.Done()
}(i)
}
wg.Wait()
}
- 第二段
func main() {
runtime.GOMAXPROCS(1)
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go func(n int) {
print(n, " ")
wg.Done()
}(i)
}
wg.Wait()
}
- 第三段
func main() {
runtime.GOMAXPROCS(1)
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go func(n int) {
time.Sleep(time.Second)
print(n, " ")
wg.Done()
}(i)
}
wg.Wait()
}
其中前两段代码是无意中看到的一道go面试题中的,虽然也看到了一些对于二者输出结果的解释,但感觉还是不够清晰,于是自己做了一些尝试,并无意中发现了第三段代码。好,下面先给出这三段代码相应的输出:
- 第一段:0到4的乱序输出
- 第二段:4 0 1 2 3
- 第三段:3 4 0 1 2
探索
- 第一段的乱序输出是很好理解的,CPU是多核的,创建了多个goroutine之后,每个goroutine得到执行的顺序无法保证,所以会出现0到4的乱序输出。
- 第二段输出的循序也是很好理解的,由于设置了单核执行,goroutine根据创建顺序依次执行,但问题在于最后创建的goroutine最先执行了。
一顿搜索查阅之后,知道了大概原因,但总觉得不够清晰,最终找到了以下从P中获取G(go源码中表示goroutine的数据结构,关于G、P、M网上有很多大佬的