Golang关键字go

1  结果为什么不是10个10?
func main() {
    runtime.GOMAXPROCS(1)
    for i := 0; i < 10; i++ {
        go print(i, ",")
    }
    runtime.Gosched()
    time.Sleep(time.Second)
}//9,0,1,2,3,4,5,6,7,8和0,1,2,3,4,5,6,7,8,9

2 从源码入手

runtime. GOMAXPROCS(1) 强行指定了只创建一个 P 来处理并发 这使得例子中的 10 个 goroutine 会是串行的

2.1 知其然 知其所以然
小心处理 for 循环中变量
func main() {
  runtime.GOMAXPROCS(1)
  for i := 0; i < 10; i++ {
      go func() {
       print(i, ",")
      }()
  }
  runtime.Gosched()
  time.Sleep(time.Second)
}//10,10,10,10,10,10,10,10,10,10 显示 10个10

区别仅仅是 go 关键字后的函数有区别 go 是创建了 goroutine
但是对于计算机而言 goroutine 只是语言封装的语法糖而已 对于计算机依旧是识别指令及内存里的值
那么 goroutine 在被创建后 留给计算机是什么样的内存布局(数据结构)呢?

在 \Go\src\runtime\runtime2.go:338 (type g struct) 定义
编译器会把 go 后面的方法和参数打包在 goroutine 里

type g struct {
    ...
    FuncVal*    fnstart; // goroutine运行的函数
    void*    param;      // 用于传递参数 睡眠时其它goroutine设置param 唤醒时此  goroutine 可以获取
    ...
}
也就是说 运行到 go 的时候 编译器就已经把 goroutine 需要运行的参数与方法都保存了
demo 1 来说就是保存了 { println, current_i }
demo 2 保存的是 { main.func_xxx, nil } 并没有把 i 传入到匿名函数中 但是引用的时候并没有发生 panic
为什么?因为这里有内存逃逸 这也是为什么 demo 2 会输出 10 个 10 的原因
编译器会把 go 后面跟着的参数与函数都打包成了对象 等待系统调度

2.2 为什么
虽说 GOMAXPROCS 为 1 导致所有的 goroutine 变成了串行 结果也不是 0 ~ 9 为什么 ?
go 在把 goroutine 放入队列(go sched 内容会有另外的篇幅来说明) 时 还做了一件很特别的事 proc:4799 (next) 代码
if next {
retryNext:
    oldnext := _p_.runnext
    if !_p_.runnext.cas(oldnext, guintptr(unsafe.Pointer(gp))) {
        goto retryNext
    }
    if oldnext == 0 {
        return
    }
    // Kick the old runnext out to the regular run queue.
    gp = oldnext.ptr()
} 意思是 go 会把每个 P 所管理的最后一个 goroutine 放入 next 位置 为什么 ?
 go 设计认为或 有过测试 如果一个 P 的 goroutine 队列在顺序执行的时候 因为 go sched 会有很多抢占或者调度 那么从被执行的概率上来分析的话 放入一个 next 位置可使得每个 goroutine 的执行概率是相当的 这个 next 位置也就解释了 demo 1 的结果为什么会是 9 0~8 到这已经说明了前面的问题 但是 demo 中出现了

runtime.Gosched()// 让出CPU时间片 让出当前goroutine的执行权限 调度器安排其它等待的任务运行 并在下次某个时候从该位置恢复执行

这一行代码。
如果注释掉结果会怎样?//0,1,2,3,4,5,6,7,8,9
如果把这一行换成 runtime.Goexit() 或者 os.Exit(0) 又会是如何呢?