package mainimport "fmt"func main() {go func() {fmt.Println()}()}
以上的代码中通过 go 关键字生成了协程,我们可以反编译找到具体的函数
go tool compile -S -N -l main.go// 可以通过 go tool compile 查询 具体的参数的意思// -S 是打印编译列表// -N 是禁止编译器优化// -l 是禁止内联
我们可以得到 
因此我们跳转到 runtime.newproc(SB)
func newproc(siz int32, fn *funcval) {argp := add(unsafe.Pointer(&fn), sys.PtrSize)gp := getg() // 这个是获取当前 gpc := getcallerpc()systemstack(func() {newproc1(fn, argp, siz, gp, pc) // 这里是真正的创建一个 go 协程})}func newproc1(fn *funcval, argp unsafe.Pointer, narg int32, callergp *g, callerpc uintptr) {_g_ := getg() //获取当前 G 的指针_p_ := _g_.m.p.ptr() // 获取当前 G 对应的 P 的指针newg := gfget(_p_)// 查询是否可以复用的 G ,可以看下文if newg == nil { //如果找不到,则创建一个新 Gnewg = malg(_StackMin) // 这里就是创建新 G 的核心部分...} ...}// 先查询是否有可以复用的 Gfunc gfget(_p_ *p) *g {retry:// 首先 _p_.gFree 表示其本地的可用的 G ,sched.gFree 表示全局可用的 Gif _p_.gFree.empty() && (!sched.gFree.stack.empty() || !sched.gFree.noStack.empty()) {lock(&sched.gFree.lock)// Move a batch of free Gs to the P.for _p_.gFree.n < 32 { // 如果本地可用的 G 的数量小于 32 ,则将全局中的 G 加入到本地 P 中 ,直到本地 P 数量到达 32 或者 全局 G 无可用的// 优先选择存在栈的 Ggp := sched.gFree.stack.pop()if gp == nil {gp = sched.gFree.noStack.pop()if gp == nil {break}}sched.gFree.n--_p_.gFree.push(gp)_p_.gFree.n++}unlock(&sched.gFree.lock)goto retry}gp := _p_.gFree.pop()if gp == nil {return nil //如果都没有可用的 G ,则返回nil}...return gp}
总结: Golang 中的 G 是可以复用的,来源分别是本地 P 和全局 G ,而且当两者都没有可用的 G 的时候,才会新建
