请教一下两个for内两个协程的执行顺序及结果的原因?

package main

import (
    "fmt"
    "runtime"
    "sync"
)

func main() {
    runtime.GOMAXPROCS(1)
    wg := sync.WaitGroup{}
    wg.Add(20)
    for i := 0; i < 10; i++ {
        go func() {
            fmt.Println("i1: ", i)
            wg.Done()
        }()
    }
    for i := 0; i < 10; i++ {
        go func(i int) {
            fmt.Println("i2: ", i)
            wg.Done()
        }(i)
    }
    wg.Wait()
}

结果

i2:  9
i1:  10
i1:  10
i1:  10
i1:  10
i1:  10
i1:  10
i1:  10
i1:  10
i1:  10
i1:  10
i2:  0
i2:  1
i2:  2
i2:  3
i2:  4
i2:  5
i2:  6
i2:  7
i2:  8
野生菌
最佳答案

关于执行顺序这个问题可以网上找一下详细的 Go 协程的设计模型—— GMP(Goroutine, Machine, Processer) 模型

然后简单回答一下楼主当前的疑惑,通过代码可知:

  1. runtime.GOMAXPROCS(1) 设置了 P 只有一个,即程序只能运行一个 协程(Goroutine)
  2. 主协程 main 被挂载在这个唯一 P 中,
  3. 两个 for 循环创建了 20 个协程(Goroutine),因为 P 正在被占用,所有的 Goroutine 依次挂在这个 P 的待执行的队列中
    1. 这里需要关注一下协程的创建与挂载在 P 下的逻辑。
      1. 因为执行结果可见,如果忽视第一行结果可见,这个 Goroutine 的执行是 FIFO (先进先出)的关系
      2. 而第一行结果出现的原因是,P 有两个队列,一个是这个 FIFO 的队列,还有一个叫做 runnext 的队列。新创建的 Goroutine 会先被挂在这个 runnext 中。
  4. 执行到 wg.Wait() 时,主协程让出,开始执行 P 的队列中的 Goroutine,即,runnext,和 FIFO 的队列

结果原因:为什么两个 for 一边全是 10,而一遍是 0,1,2…

因为:创建协程的时候,变量在协程中的地址在第一个全是 for 的循环中是同一个,而这个值在 for 的执行过程中被修改为了 10. 而第二个 for 循环创建的协程中打印的这个变量是通过值传递传递的,则每个协程中的这个 i 的地址都是全新的,不一样的,即值是不一的

2年前 评论
讨论数量: 3
野生菌

查阅资料可知: 首先,第一个for一定是打印10,第二个for打印是0、1、2...到9。 那么当前go版本为1.18.9,每起一个协程时,会对应一个函数地址“p.runnext”和先进先出的队列“p.runq”。 则当所有for跑完时,到了wait后就执行第一个协程“p.runnext”为“9”,那么接下来执行第一个for为10、10...,然后第二个for为1、2、3、4....8

2年前 评论

第一个是个闭包,第二个是拷贝

2年前 评论

关于执行顺序这个问题可以网上找一下详细的 Go 协程的设计模型—— GMP(Goroutine, Machine, Processer) 模型

然后简单回答一下楼主当前的疑惑,通过代码可知:

  1. runtime.GOMAXPROCS(1) 设置了 P 只有一个,即程序只能运行一个 协程(Goroutine)
  2. 主协程 main 被挂载在这个唯一 P 中,
  3. 两个 for 循环创建了 20 个协程(Goroutine),因为 P 正在被占用,所有的 Goroutine 依次挂在这个 P 的待执行的队列中
    1. 这里需要关注一下协程的创建与挂载在 P 下的逻辑。
      1. 因为执行结果可见,如果忽视第一行结果可见,这个 Goroutine 的执行是 FIFO (先进先出)的关系
      2. 而第一行结果出现的原因是,P 有两个队列,一个是这个 FIFO 的队列,还有一个叫做 runnext 的队列。新创建的 Goroutine 会先被挂在这个 runnext 中。
  4. 执行到 wg.Wait() 时,主协程让出,开始执行 P 的队列中的 Goroutine,即,runnext,和 FIFO 的队列

结果原因:为什么两个 for 一边全是 10,而一遍是 0,1,2…

因为:创建协程的时候,变量在协程中的地址在第一个全是 for 的循环中是同一个,而这个值在 for 的执行过程中被修改为了 10. 而第二个 for 循环创建的协程中打印的这个变量是通过值传递传递的,则每个协程中的这个 i 的地址都是全新的,不一样的,即值是不一的

2年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!