// One round of scheduler: find a runnable goroutine and execute it.// Never returns.funcschedule(){_g_:=getg()//_g_ = m.g0......vargp*g......ifgp==nil{// Check the global runnable queue once in a while to ensure fairness.// Otherwise two goroutines can completely occupy the local runqueue// by constantly respawning each other.//为了保证调度的公平性,每个工作线程每进行61次调度就需要优先从全局运行队列中获取goroutine出来运行,//因为如果只调度本地运行队列中的goroutine,则全局运行队列中的goroutine有可能得不到运行if_g_.m.p.ptr().schedtick%61==0&&sched.runqsize>0{lock(&sched.lock)//所有工作线程都能访问全局运行队列,所以需要加锁gp=globrunqget(_g_.m.p.ptr(),1)//从全局运行队列中获取1个goroutineunlock(&sched.lock)}}ifgp==nil{//从与m关联的p的本地运行队列中获取goroutinegp,inheritTime=runqget(_g_.m.p.ptr())ifgp!=nil&&_g_.m.spinning{throw("schedule: spinning with local work")}}ifgp==nil{//如果从本地运行队列和全局运行队列都没有找到需要运行的goroutine,//则调用findrunnable函数从其它工作线程的运行队列中偷取,如果偷取不到,则当前工作线程进入睡眠,//直到获取到需要运行的goroutine之后findrunnable函数才会返回。gp,inheritTime=findrunnable()// blocks until work is available}......//当前运行的是runtime的代码,函数调用栈使用的是g0的栈空间//调用execte切换到gp的代码和栈空间去运行execute(gp,inheritTime)}
// Try get a batch of G's from the global runnable queue.
// Sched must be locked.
func globrunqget(_p_ *p, max int32) *g {
if sched.runqsize == 0 { //全局运行队列为空
return nil
}
//根据p的数量平分全局运行队列中的goroutines
n := sched.runqsize / gomaxprocs + 1
if n > sched.runqsize { //上面计算n的方法可能导致n大于全局运行队列中的goroutine数量
n = sched.runqsize
}
if max > 0 && n > max {
n = max //最多取max个goroutine
}
if n > int32(len(_p_.runq)) / 2 {
n = int32(len(_p_.runq)) / 2 //最多只能取本地队列容量的一半
}
sched.runqsize -= n
//直接通过函数返回gp,其它的goroutines通过runqput放入本地运行队列
gp := sched.runq.pop() //pop从全局运行队列的队列头取
n--
for ; n > 0; n-- {
gp1 := sched.runq.pop() //从全局运行队列中取出一个goroutine
runqput(_p_, gp1, false) //放入本地运行队列
}
return gp
}
// Get g from local runnable queue.
// If inheritTime is true, gp should inherit the remaining time in the
// current time slice. Otherwise, it should start a new time slice.
// Executed only by the owner P.
func runqget(_p_ *p) (gp *g, inheritTime bool) {
// If there's a runnext, it's the next G to run.
//从runnext成员中获取goroutine
for {
//查看runnext成员是否为空,不为空则返回该goroutine
next := _p_.runnext
if next == 0 {
break
}
if _p_.runnext.cas(next, 0) {
return next.ptr(), true
}
}
//从循环队列中获取goroutine
for {
h := atomic.LoadAcq(&_p_.runqhead) // load-acquire, synchronize with other consumers
t := _p_.runqtail
if t == h {
return nil, false
}
gp := _p_.runq[h%uint32(len(_p_.runq))].ptr()
if atomic.CasRel(&_p_.runqhead, h, h+1) { // cas-release, commits consume
return gp, false
}
}
}