Go 入门(19):goroutine 基础——go 关键字创建轻量级线程
更新时间:2026-09-01。本文是
languages/go/beginner/入门层第 19 篇,接 单元测试。goroutine 是 Go 的并发基础,用go关键字启动一个函数调用,就创建了一个 goroutine。百万级并发,就是 Go 的招牌。
本文要回答的问题
- goroutine 和线程有什么区别?为什么能开上百万个?
go f()是个什么操作?函数执行完会怎样?- 主 goroutine 退出后,其他 goroutine 还在吗?
- GOMAXPROCS 控制什么?默认值是多少?
一、go 关键字启动 goroutine
package main
import (
"fmt"
"time"
)
func say(s string) {
for i := 0; i < 3; i++ {
fmt.Println(s)
time.Sleep(100 * time.Millisecond)
}
}
func main() {
go say("hello") // 启动 goroutine 跑 say
say("world") // 主 goroutine 跑
time.Sleep(500 * time.Millisecond) // 等一会儿,让 goroutine 跑完
}go say("hello") 在一个新的 goroutine 里执行 say("hello"),主 goroutine 继续执行后面的代码。两个 goroutine 并发运行。
二、goroutine vs 线程
| 对比 | OS 线程 | goroutine |
|---|---|---|
| 创建栈大小 | 1~8 MB(固定) | 2 KB(动态增长) |
| 创建/切换成本 | 内核态系统调用,µs 级 | 用户态,ns 级 |
| 数量上限 | 数千 | 百万级 |
| 调度 | 内核调度器 | Go runtime 调度器 |
为什么 goroutine 轻?因为 Go runtime 在用户态管理 goroutine,多个 goroutine 复用到少量 OS 线程上(M:N 调度)。
三、主 goroutine 退出,其他 goroutine 也退出
main() 函数运行在主 goroutine 上,主 goroutine 退出时,程序就结束了——其他 goroutine 不管有没有跑完,都会终止:
func main() {
go func() {
for {
fmt.Println("running...")
}
}()
// 主 goroutine 直接退出,上面的 goroutine 也就停了
}所以入门阶段需要在 main 里加 time.Sleep 或 sync.WaitGroup 来等待 goroutine 完成。
四、GOMAXPROCS
GOMAXPROCS 控制 Go 程序同时执行用户 Go 代码的最大 CPU 数,默认是机器的 CPU 核心数:
import "runtime"
fmt.Println(runtime.GOMAXPROCS(0)) // 打印当前值,机器是 8 核就输出 8
runtime.GOMAXPROCS(1) // 设置成单核- 默认值 = CPU 核心数,充分利用多核
- 只设 1 就是单核,用于调试
- 不是"最多同时运行 N 个 goroutine",而是"最多同时有 N 个 goroutine 在 OS 线程上跑"
五、goroutine 泄漏:最常见的并发 bug
func main() {
ch := make(chan int)
go func() {
val := <-ch // 永远等不到数据
fmt.Println(val)
}()
// 没有往 ch 发数据,也没 close
// goroutine 一直阻塞,泄漏了
}goroutine 泄漏就是goroutine 一直阻塞,永远不退出,但 GC 不会回收它。泄漏多了,内存暴涨,最终 OOM。
泄漏常见原因:
- channel 发送/接收不匹配,阻塞
- 在 select 里没有 timeout
- 忘记停止 ticker
六、常见坑对照
| 坑 | 现象 | 对策 |
|---|---|---|
| 主 goroutine 退出程序结束 | 其他 goroutine 没跑完 | 用 WaitGroup 等待 |
| goroutine 泄漏 | 内存持续增长 | 确保 goroutine 最终能退出 |
| 捕获循环变量 | 闭包拿到最后一个值 | v := v 创建副本 |
| 不知道 goroutine 什么时候跑完 | 程序结束了 goroutine 还没跑完 | 用 channel 或 WaitGroup 同步 |
相关与延伸
下一篇:sync.WaitGroup——等待并发完成;goroutine 的 GMP 调度模型,进阶层 goroutine 与 channel 深度讲。
一句话总结
Go goroutine:go f() 启动 goroutine,初始栈 2 KB,动态增长,百万级并发没问题;主 goroutine 退出程序结束,要等 goroutine 干完用 WaitGroup;GOMAXPROCS 默认 CPU 核心数,控制同时执行 goroutine 的 OS 线程数;goroutine 泄漏是常见 bug,确保 goroutine 最终能退出。