作者小头像 Lv.2
60 成长值

个人介绍

这个人很懒,什么都没有留下

感兴趣或擅长的领域

暂无数据
个人勋章
  • 活跃之星
成长雷达
15
30
0
15
0

个人资料

个人介绍

这个人很懒,什么都没有留下

感兴趣或擅长的领域

暂无数据

达成规则

发布时间 2026/10/22 16:09:03 最后回复 yd_216289496 2026/10/22 16:12:55 版块 开发者空间
9 1 0
他的回复:
goroutine 轻量的核心不是“神奇地不占资源”,而是把线程模型里最贵的三件事拆开、搬到用户态并按需分配。1. 栈按需增长:goroutine 初始栈通常只有约 2KB,运行中按需成倍扩缩容;线程栈则由内核预留,常见默认 1~8MB,创建时就要占地址空间和物理页。2. 切换在用户态:G 的挂起/恢复只需要保存少量寄存器到 gobuf,由 Go runtime 调度,不进入内核态;线程上下文切换要陷入内核,还会影响 TLB 和 cache,成本高一个数量级。3. G-M-P 的分工:- G(goroutine):执行上下文,保存栈、程序计数器、状态和调度信息;- M(machine):真正执行代码的 OS 线程,必须绑定一个 P 才能执行 Go 代码;- P(processor):逻辑处理器,持有本地可运行队列(通常 256 个槽位)、mcache 等,数量由 GOMAXPROCS 决定,决定并行度。runtime 用“本地队列 + 全局队列 + work stealing”把 G 分给空闲 P。M 因阻塞系统调用停住时会把 P 交给其他 M(handoff);网络 I/O 由 netpoller 就绪后唤醒 G;sysmon 负责监控和抢占。因此大量 G 可以复用少量 M,单个阻塞不会拖死整个程序。4. 代价与边界:goroutine 仍要占栈和调度元数据,十万级通常没问题,百万级要评估内存;CPU 密集任务不会因为 G 多而变快,仍受 GOMAXPROCS 限制;阻塞式 cgo 调用会占用 M;最常见的坑是 goroutine 泄漏(channel 阻塞、忘记 cancel),可用 context 控制生命周期,用 pprof 的 goroutine profile 排查。一句话:G 是用户态、可增长栈的执行单元,M 是内核线程,P 是调度上下文和并行配额;轻量来自用户态调度、小栈和复用 M,而不是零成本。