返回介绍

上卷 程序设计

中卷 标准库

下卷 运行时

源码剖析

附录

4.5.2 查找

发布于 2024-10-12 19:15:58 字数 2766 浏览 0 评论 0 收藏 0

调度函数依次查找本地、全局队列,以及通过轮询(netpoll)返回的事件任务。

有关本地、全局队列,任务偷窃等内容,参见《4.3.4 任务》。

// proc.go

// Finds a runnable goroutine to execute.
// Tries to steal from other P's, get g from local or global queue, poll network.

func findrunnable() (gp *g, inheritTime bool) {
	_g_ := getg()

	// The conditions here and in handoffp must agree: if
	// findrunnable would return a G to run, handoffp must start
	// an M.

top:
	_p_ := _g_.m.p.ptr()
    
    // STW!!!
	if sched.gcwaiting != 0 {
		gcstopm()
		goto top
	}
    
    // 类似 forEachP 等任务。
	if _p_.runSafePointFn != 0 {
		runSafePointFn()
	}

    // 检查并执行定时器。
	now, pollUntil, _ := checkTimers(_p_, 0)

    // 唤醒 Finalizer。
	if fingwait && fingwake {
		if gp := wakefing(); gp != nil {
			ready(gp, 0, true)
		}
	}

	// 检查本地队列。
	if gp, inheritTime := runqget(_p_); gp != nil {
		return gp, inheritTime
	}

	// 检查全局队列。
	if sched.runqsize != 0 {
		lock(&sched.lock)
		gp := globrunqget(_p_, 0)
		unlock(&sched.lock)
		if gp != nil {
			return gp, false
		}
	}

    // 网络轮询。
	if netpollinited() && atomic.Load(&netpollWaiters) > 0 && atomic.Load64(&sched.lastpoll) != 0 {
		if list := netpoll(0); !list.empty() { // non-blocking
			gp := list.pop()
			injectglist(&list)
			casgstatus(gp, _Gwaiting, _Grunnable)
            
			return gp, false
		}
	}

    // 从其他 Ps 偷窃。
	procs := uint32(gomaxprocs)
	if _g_.m.spinning || 2*atomic.Load(&sched.nmspinning) < procs-atomic.Load(&sched.npidle) {
		if !_g_.m.spinning {
			_g_.m.spinning = true
			atomic.Xadd(&sched.nmspinning, 1)
		}

		gp, inheritTime, tnow, w, newWork := stealWork(now)
		now = tnow
		if gp != nil {
			// Successfully stole.
			return gp, inheritTime
		}
		if newWork {
			// There may be new timer or GC work; restart to
			// discover.
			goto top
		}
		if w != 0 && (pollUntil == 0 || w < pollUntil) {
			// Earlier timer to wait for.
			pollUntil = w
		}
	}

    // 无事可做,给 GC 打工。
	if gcBlackenEnabled != 0 && gcMarkWorkAvailable(_p_) {
		node := (*gcBgMarkWorkerNode)(gcBgMarkWorkerPool.pop())
		if node != nil {
			_p_.gcMarkWorkerMode = gcMarkWorkerIdleMode
			gp := node.gp.ptr()
			casgstatus(gp, _Gwaiting, _Grunnable)
            
			return gp, false
		}
	}

	// 休眠前再次检查!!!
	lock(&sched.lock)
    
	if sched.gcwaiting != 0 || _p_.runSafePointFn != 0 {
		unlock(&sched.lock)
		goto top
	}
	if sched.runqsize != 0 {
		gp := globrunqget(_p_, 0)
		unlock(&sched.lock)
		return gp, false
	}
    
    // 释放 P,放回闲置队列。
	if releasep() != _p_ {
		throw("findrunnable: wrong p")
	}
	pidleput(_p_)
    
	unlock(&sched.lock)

	// Poll network until next timer ...
    
    // 休眠,等待唤醒后再次查找。
	stopm()
    
	goto top
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文