原文地址:Goroutine运行时间过长而发生的抢占调度详解~
本文主要关注以下两点:
-
发生抢占调度的情况。
-
因运行时间过长发生的抢占调度的特点。
sysmon系统监控线程会定期(10毫秒)通过retake对goroutine发起抢占。
来看runtime/proc.go文件4376行分析retake:
// forcePreemptNS is the time slice given to a G before it is// preempted.const forcePreemptNS = 10 * 1000 * 1000 // 10msfunc retake(now int64) uint32 {n := 0// Prevent allp slice changes. This lock will be completely// uncontended unless we're already stopping the world.lock(&allpLock)// We can't use a range loop over allp because we may// temporarily drop the allpLock. Hence, we need to re-fetch// allp each time around the loop.for i := 0; i < len(allp); i++ { //遍历所有的P_p_ := allp[i]if _p_ == nil {// This can happen if procresize has grown// allp but not yet created new Ps.continue}//_p_.sysmontick用于sysmon线程记录被监控p的系统调用时间和运行时间pd := &_p_.sysmonticks := _p_.statusif s == _Psyscall { //P处于系统调用之中,需要检查是否需要抢占// Retake P from syscall if it's there for more than 1 sysmon tick (at least 20us).t := int64(_p_.syscalltick)if int64(pd.syscalltick) != t {pd.syscalltick = uint32(t)pd.syscallwhen = nowcontinue}// On the one hand we don't want to retake Ps if there is no other work to do,// but on the other hand we want to retake them eventually// because they can prevent the sysmon thread from deep sleep.if runqempty(_p_) && atomic.Load(&sched.nmspinning)+atomic.Load(&sched.npidle) > 0 && pd.syscallwhen+10*1000*1000 > now {continue}// Drop allpLock so we can take sched.lock.unlock(&allpLock)// Need to decrement number of idle locked M's// (pretending that one more is running) before the CAS.// Otherwise the M from which we retake can exit the syscall,// increment nmidle and report deadlock.incidlelocked(-1)if atomic.Cas(&_p_.status, s, _Pidle) {if trace.enabled {traceGoSysBlock(_p_)traceProcStop(_p_)}n++

最低0.47元/天 解锁文章
864

被折叠的 条评论
为什么被折叠?



