go语句后必须跟函数调用,不能跟变量或表达式;协程启动后主goroutine退出则程序终止,需用sync.waitgroup或channel同步。

go 语句后面必须跟函数调用,不能跟表达式或变量
很多人写 go someVar 或 go x + y,直接 panic:「cannot use … as value in go statement」。Go 要求 go 后面必须是可执行的函数调用(包括匿名函数),本质是启动一个新协程去运行该函数体。
-
go doWork()✅ 正确:调用函数 -
go func() { ... }()✅ 正确:立即执行的匿名函数 -
go doWork❌ 错误:只是函数值,没调用 -
go fmt.Println("hello")✅ 可以,但注意:这会立即求值并启动协程,不是延迟执行
goroutine 启动后不等返回,主 goroutine 退出整个程序就结束了
新手常写完 go doSomething() 就直接 return 或函数结束,发现什么都没打印——因为 main 函数退出,所有 goroutine 被强制终止。这不是 bug,是 Go 的设计:没有“后台线程”概念,只有“谁活谁干”。
- 调试时加
time.Sleep(time.Second)看效果,但生产环境绝不能这么干 - 真正可靠的方式是用
sync.WaitGroup等待完成 - 或者用
channel接收完成信号,比如done := make(chan struct{})+go func() { ...; done
闭包捕获循环变量时容易全部拿到最后一个值
在 for 循环里用 go func() { fmt.Println(i) }(),最后可能全输出相同数字(比如全是 10)。这是因为所有 goroutine 共享同一个变量 i 的地址,等它们真正执行时,循环早已结束,i 已是终值。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 正确做法:把当前值传进 goroutine,如
go func(val int) { fmt.Println(val) }(i) - 或在循环内定义新变量:
for i := range items { i := i; go func() { ... }() }(利用短变量声明创建新绑定) - Go 1.22+ 支持更直观的循环变量作用域,但老版本仍需手动处理
goroutine 泄漏比想象中常见,别以为“小”就没事
一个没接收的 chan、一个没关闭的 http.Client 连接、一个忘了 close() 的管道,都可能让 goroutine 卡在阻塞状态,永远不退出。pprof 查 runtime.Goroutines() 常能看到几百上千个“僵尸”。
- 用
runtime.NumGoroutine()在关键点打日志,观察是否持续增长 - HTTP 请求务必设超时:
http.DefaultClient.Timeout = 5 * time.Second - channel 操作前确认 sender/receiver 是否还活着,必要时用
select+default防死锁
goroutine 本身开销不大,但泄漏会累积资源(内存、文件描述符、TCP 连接),而且一旦出问题,排查比加 go 那行代码难十倍。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










