在 Go 语言中,Goroutine 是并发模型的核心,而 P(Processor) 是 Go 调度器中的一个关键抽象。理解 Goroutine 调度模型 中的 G(Goroutine)、M(Machine,内核线程)、P(Processor,逻辑处理器) 的关系可以帮助我们理解 Go 的抢占式调度策略。
Go 调度器使用 G-M-P 模型:
P
是逻辑处理器,负责调度和管理 Goroutine,最多有 GOMAXPROCS
个 P
。每个 P
可以运行一个 Goroutine
。M
是操作系统的内核线程。每个 M
需要绑定一个 P
来执行 Goroutine
。Go 的调度器采用的是协作式调度为主,抢占式调度为辅。协作式调度意味着 Goroutine 需要主动放弃控制权来让其他 Goroutine 运行,比如调用系统调用或者 Goroutine 自己调用 runtime.Gosched()
。
抢占式调度则是为了防止某些 Goroutine 占用 CPU 太久(比如某个 Goroutine 在长时间执行计算密集型任务),Go 1.14 引入了针对 计算密集型 Goroutine 的 抢占式调度。抢占式调度可以在以下场景下触发:
P
的时机P
,此时调度器可能会选择调度其他的 Goroutine 来运行。下面的例子展示了一个 Goroutine 在执行计算密集型任务时如何可能会被 Go 的抢占式调度机制打断。
package main
import (
"fmt"
"runtime"
"time"
)
// 模拟一个计算密集型任务
func busyLoop() {
for i := 0; i < 1e10; i++ {
// 占用 CPU,但没有主动让出调度权
}
fmt.Println("Finished busy loop")
}
func main() {
runtime.GOMAXPROCS(1) // 设置只有 1 个 P
go func() {
for {
fmt.Println("Running another goroutine...")
time.Sleep(500 * time.Millisecond) // 每 500 毫秒休息一次
}
}()
busyLoop() // 执行计算密集型任务
time.Sleep(2 * time.Second)
}
runtime.GOMAXPROCS(1)
:我们将 GOMAXPROCS
设置为 1,意味着整个程序中只有一个 P
,这样所有 Goroutine 都只能在这个 P
上调度。busyLoop
:这是一个计算密集型任务,在没有主动进行系统调用或让出调度权的情况下,循环执行大量的操作,耗尽 CPU 时间。busyLoop
没有主动让出 CPU,但由于 Go 的抢占式调度机制,调度器可能会在合适的时间点打断 busyLoop
,让其他 Goroutine(比如打印 "Running another goroutine..." 的那个 Goroutine)得到执行机会。Running another goroutine...
Running another goroutine...
...
Finished busy loop
我们可以看到,尽管 busyLoop
是一个计算密集型任务,其他的 Goroutine 仍然会间歇性地被调度并执行。这个就是 Go 抢占式调度的效果。
抢占式调度的核心机制是 定期检查 Goroutine 的执行时间。Go 调度器在后台维护一个时间戳,记录 Goroutine 上次被调度的时间。调度器每隔一段时间会检查当前运行的 Goroutine,如果 Goroutine 占用了 CPU 超过一定时间,调度器就会标记这个 Goroutine 需要被抢占,然后调度其他的 Goroutine 来执行。
抢占式调度通过以下方式触发:
我们可以通过使用 GODEBUG
环境变量,启用抢占式调度的调试日志,观察抢占调度的具体行为。运行如下代码时,启用调试模式:
GODEBUG=schedtrace=1000,scheddetail=1 go run main.go
schedtrace=1000
表示每隔 1000 毫秒输出一次调度器状态。scheddetail=1
表示输出详细的调度器信息。在输出的调试信息中,我们可以看到调度器何时抢占了 Goroutine,何时让出了 P
,以及具体的调度行为。调试信息会包括如下内容:
M
(线程)变成空闲状态。P
。P
中窃取任务来运行。这使得 Go 语言能更加高效地运行并发程序,避免单个 Goroutine 长时间霸占 CPU,影响其他 Goroutine 的执行。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。