Go1.18 调度器-G复用
发表于 · 归类于
代码 · 阅读完需 8 分钟 ·
报告错误 ·
阅读:
复用
对于执行结束的 dead G
并不会释放,而是缓存在P本地或全局,以待复用。
// runtime2.go
type schedt struct {
// Global cache of dead G's.
gFree struct {
lock mutex
stack gList // Gs with stacks
noStack gList // Gs without stacks
n int32
}
}
type p struct {
// Available G's (status == Gdead)
gFree struct {
gList
n int32
}
}
// runtime2.go, proc.go
// A gList is a list of Gs linked through g.schedlink. A G can only be
// on one gQueue or gList at a time.
type gList struct {
head guintptr
}
type g struct {
schedlink guintptr
}
type guintptr uintptr
获取
从本地获取。如果为空,则从全局转移一批到本地。
// proc.go
// Get from gfree list.
// If local list is empty, grab a batch from global list.
func gfget(_p_ *p) *g {
retry:
// 本地为空,从全局转移一批后重试。
if _p_.gFree.empty() && (!sched.gFree.stack.empty() || !sched.gFree.noStack.empty()) {
lock(&sched.gFree.lock)
// 直到本地缓存 32 个。
for _p_.gFree.n < 32 {
// 优先选择带栈内存的。
gp := 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
}
_p_.gFree.n--
// 分配栈内存。
if gp.stack.lo == 0 {
// Stack was deallocated in gfput. Allocate a new one.
systemstack(func() {
gp.stack = stackalloc(_FixedStack)
})
gp.stackguard0 = gp.stack.lo + _StackGuard
}
return gp
}
放回
直接放回本地缓存。如果本地过多,则转移到全局,以供其他P使用。
// proc.go
// Put on gfree list.
// If local list is too long, transfer a batch to the global list.
func gfput(_p_ *p, gp *g) {
// 如果栈扩张过,则释放其内存。
stksize := gp.stack.hi - gp.stack.lo
if stksize != _FixedStack {
// non-standard stack size - free it.
stackfree(gp.stack)
gp.stack.lo = 0
gp.stack.hi = 0
gp.stackguard0 = 0
}
// 放回本地链表。
_p_.gFree.push(gp)
_p_.gFree.n++
// 本地数量过多。
if _p_.gFree.n >= 64 {
var (
inc int32
stackQ gQueue
noStackQ gQueue
)
// 提取多余的,存为链表并转移到全局。
for _p_.gFree.n >= 32 {
gp = _p_.gFree.pop()
_p_.gFree.n--
// 根据是否有栈内存,安排不同链表。
if gp.stack.lo == 0 {
noStackQ.push(gp)
} else {
stackQ.push(gp)
}
inc++
}
lock(&sched.gFree.lock)
sched.gFree.noStack.pushAll(noStackQ)
sched.gFree.stack.pushAll(stackQ)
sched.gFree.n += inc
unlock(&sched.gFree.lock)
}
}