419 字
2 分钟
Golang GMP 调度器详解:Go 语言高并发的内核
协程 (Coroutine) 的复兴
在 Go 语言出现之前,高并发要么靠多进程(PHP),要么靠多线程(Java),要么靠异步回调(Node.js)。
Go 选择了 M
GMP 模型
- G (Goroutine):协程。包含栈、指令指针等。初始栈只有 2KB。
- M (Machine):内核线程 (OS Thread)。真正执行代码的劳工。
- P (Processor):逻辑处理器。代表 CPU 核心。它维护了一个本地队列 (Local Run Queue)。
调度流程
- M 绑定 P:线程 M 必须拿到 P 才能执行 G。
- 本地队列:新创建的 G 优先放在 P 的本地队列,无锁,速度快。
- 全局队列:如果本地满了,放在全局队列(需要加锁)。
- Work Stealing (窃取):如果 P 的本地队列空了,它会去偷其他 P 的一半 G 过来运行。这就保证了所有 CPU 核心都不会闲着。
系统调用 (Syscall)
当 G 进行系统调用(如文件 IO)阻塞时:
- P 会和 M 分离 (Handoff)。P 去找一个新的 M(或者新建一个)继续运行其他的 G。
- 旧的 M 陪着那个阻塞的 G 等待。
- 这就避免了整个线程被阻塞导致 CPU 浪费。
抢占式调度
在 Go 1.14 之前,如果一个 G 写了死循环,它会霸占 P,其他 G 饿死。 现在,Go 引入了 基于信号的抢占。 Runtime 会向线程发送信号,强行中断 G 的执行,把它踢回队列。
总结
Goroutine 使得“每请求一线程”成为可能。
你只管 go func(),剩下的交给 GMP。
这是 Go 语言称霸云原生领域的基石。
Golang GMP 调度器详解:Go 语言高并发的内核
https://www.oferry.com/posts/a63/