go关键字后必须跟函数调用,不能跟变量或表达式;常见错误如go f(应为go f());循环中启动goroutine需显式传参避免捕获循环变量。

go 关键字后面必须跟函数调用,不能跟表达式或变量
很多人写 go someVar 或 go someStruct.Field,这会直接编译失败:「cannot use … as value in go statement」。Go 要求 go 后面必须是一个可执行的函数调用,包括命名函数调用、方法调用,或匿名函数调用。
常见错误示例:
var f func() = func() { fmt.Println("hi") }
go f // ❌ 编译错误:f 不是调用,只是变量
go f() // ✅ 正确:这是函数调用
- 闭包传参时务必显式传递值,避免循环变量捕获陷阱
- 如果函数需要接收参数,必须在括号里写全,
go f(x, y)是合法的;go f x是非法语法 - 方法调用也适用:如
go obj.Do()、go (*obj).Method()都可以
for 循环中启动 goroutine 容易误捕获循环变量
下面这段代码会输出三次 3,而不是 0、1、2:
for i := 0; i < 3; i++ {
go func() {
fmt.Println(i) // 所有 goroutine 共享同一个 i 变量
}()
}
原因在于匿名函数捕获的是变量 i 的地址,等 goroutine 真正执行时,循环早已结束,i 值为 3。
立即学习“go语言免费学习笔记(深入)”;
- 正确做法是把当前值作为参数传入:
go func(val int) { fmt.Println(val) }(i) - 或者在循环内定义新变量:
for i := 0; i - 注意:即使用了
range,对切片/映射遍历时的索引变量也存在同样问题,不能直接闭包引用
main 函数退出会导致所有 goroutine 强制终止
这是最常被忽略的「goroutine 消失」原因。只要 main() 函数返回,整个程序立即退出,不管其他 goroutine 是否还在运行。
典型症状:只看到主线程输出,看不到 goroutine 的打印;或日志刚写一半程序就结束了。
-
time.Sleep是临时调试手段,但不可用于生产——它不保证 goroutine 已完成,只靠「猜时间」 - 正式场景必须用同步机制:
sync.WaitGroup(适合已知数量)、chan(适合结果驱动)、context.WithTimeout(带超时控制) - 如果 goroutine 内部有阻塞操作(如
http.Get、time.After),没加超时或取消逻辑,可能永远卡住,导致 WaitGroup 永不返回
goroutine 没有 ID、无法主动终止、也不返回句柄
你不能获取某个 goroutine 的 ID,也不能像线程那样 kill 或 join 它。Go 故意屏蔽了这些能力,强调「通过通信共享内存」的设计哲学。
这意味着:
- 无法做「等待第 5 个 goroutine 结束」这种精确控制
- 没有类似
pthread_cancel的机制;想让 goroutine 退出,得靠它自己检查context.Done()或从 channel 收到信号 - 不要试图用
runtime.NumGoroutine()做业务逻辑判断——它只反映当前数量,且包含 runtime 内部使用的 goroutine - goroutine 泄漏通常表现为
runtime.NumGoroutine()持续增长,根源往往是 channel 未关闭、receiver 没读完、或 context 忘记 cancel


















