Go 并发模型浅析:从 goroutine 到 GMP 调度
Go 的并发能力来源于语言层面的 goroutine 与运行时调度器。本文不贴大段源码,只讲清核心思路。
为什么 goroutine 足够轻量
goroutine 初始栈仅几 KB,按需增长;由 Go 运行时(而非操作系统)负责调度,创建与切换成本远低于线程。因此一个进程内开成千上万个 goroutine 很常见。
GMP 三件套
- G:goroutine,即一个待执行的任务单元;
- M:操作系统线程,真正执行代码的载体;
- P:处理器,持有本地可运行队列,M 必须绑定 P 才能执行 G。
调度器把 G 放到 P 的本地队列,M 从队列取 G 执行;遇到系统调用时,M 会被解绑,让出 P 给其他 M,从而减少阻塞浪费。
一个简单示例
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go func(n int) {
defer wg.Done()
fmt.Println("hello", n)
}(i)
}
wg.Wait()
}
常见误区
闭包循环变量捕获、无节制开 goroutine 导致资源耗尽、直接共享变量而不加同步,都是新手常见问题。并发编程的第一原则:能用 channel 表达就用 channel,不要裸共享内存。
← 返回首页