goroutine 是 Go 运行时管理的用户态轻量级执行单元,而非内核线程。核心差异:初始栈很小(约 2KB,可动态增长收缩),线程通常 MB 级固定栈;创建销毁在用户态完成,成本远低于系统调用;调度由 Go 运行时完成(GMP 模型),切换无需陷入内核;一个程序可轻松跑几十万 goroutine,而线程数量级通常只有几千。

运行时维护 M(OS 线程)池,大量 G 复用少量 M,通过 P 做 work-stealing 调度。goroutine 阻塞在系统调用时运行时会拆开 M 与 P,让其他 G 继续执行;channel、锁等阻塞则在用户态直接挂起 G,成本极低。

for i := 0; i < 100000; i++ {
    go func(id int) { /* ... */ }(i) // 十万级并发是常规操作
}

易错点:goroutine 便宜但不是免费,无节制创建照样耗尽内存;闭包捕获循环变量在 Go 1.22 前是经典 bug,1.22 起每次迭代都是新变量。追问方向:GMP 细节、sysmon 抢占、GOMAXPROCS 的影响。