P 会管理一组 Goroutine,并为 M 分派 Goroutine。P 会周期性地检查每个关联的 M 的运行状态,以确保它们不会长时间阻塞。
如果一个 M 长时间阻塞(例如,因为系统调用),P 可以将与该 M 关联的 Goroutine 分派给其他 M 执行,以充分利用 CPU。
当 Goroutine 执行完成或自愿让出 CPU 时,M 会从 P 获取下一个要执行的 Goroutine。
协作过程如下:
G 的状态从 Gidle(空闲)变为 Grunnable(可运行),表示它已经准备好执行。
P 的调度器会周期性地检查是否有可运行的 Goroutine。如果有,它会将一个 Goroutine 分配给一个空闲的 M。
M 从 P 获取 Goroutine,并开始执行它。当 Goroutine 遇到阻塞操作或自愿让出 CPU 时,M 会重新归还 Goroutine 并继续从 P 获取新的 Goroutine。
当 M 长时间阻塞(如系统调用)时,P 可以将关联的 Goroutine 分派给其他 M。
G 的状态可能变为 Gwaiting(等待),表示它在等待某个条件满足。一旦条件满足,G 可以重新变为 Grunnable。
通过这种方式,Goroutine 在 M 和 P 的协作下实现了高效的并发和并行执行。Goroutine 的创建和销毁成本很低,而 M 的创建和销毁会根据系统负载动态调整,使得 Golang 能够在多核处理器上充分利用硬件并行性。值得注意的是,Goroutine 的调度是由运行时自动处理的,开发者无需手动控制。这种机制使得 Golang 能够高效地支持大规模并发编程。