Go运行时调度器中,如何运行时间过长被抢占的情况?

摘要:原创文章,欢迎转载,转载请注明出处,谢谢。 0. 前言 在 Go runtime 调度器精讲(七):案例分析 一文我们介绍了一个抢占的案例。从案例分析抢占的实现,并未涉及到源码层面。本文将继续从源码入手,看 Go runtime 调度器是如
原创文章,欢迎转载,转载请注明出处,谢谢。 0. 前言 在 Go runtime 调度器精讲(七):案例分析 一文我们介绍了一个抢占的案例。从案例分析抢占的实现,并未涉及到源码层面。本文将继续从源码入手,看 Go runtime 调度器是如何实现抢占逻辑的。 1. sysmon 线程 还记得 Go runtime 调度器精讲(四):运行 main goroutine 一文我们蜻蜓点水的提了一嘴 sysmon 线程,它是运行在系统栈上的监控线程,负责监控 goroutine 的状态,并且做相应处理。当然,也负责做抢占的处理,它是本讲的重点。 sysmon 的创建在 src/runtime/proc.go:sysmon: // The main goroutine. func main() { ... if GOARCH != "wasm" { // no threads on wasm yet, so no sysmon systemstack(func() { newm(sysmon, nil, -1) }) } ... } sysmon 不需要和 P 绑定,作为监控线程运行在系统栈。进入 sysmon: func sysmon() { ... idle := 0 // how many cycles in succession we had not wokeup somebody delay := uint32(0) for { if idle == 0 { // start with 20us sleep... delay = 20 // } else if idle > 50 { // start doubling the sleep after 1ms... delay *= 2 } if delay > 10*1000 { // up to 10ms delay = 10 * 1000 } usleep(delay) // 休眠 delay us // retake P's blocked in syscalls // and preempt long running G's if retake(now) != 0 { idle = 0 } else { idle++ } ... } } 省略了很多和抢占无关的内容,和抢占相关的是 retake 函数,进入 retake: func retake(now int64) uint32 { n := 0 lock(&allpLock) // 。。。
阅读全文