goroutine 启动函数必须无参或用闭包封装参数,避免立即执行;循环中需传值防止变量复用;结果须通过 channel、waitgroup 或 errgroup 显式通信获取。

goroutine 启动函数必须是无参或闭包封装
Go 不允许直接传参给 go 关键字启动的函数,比如 go foo(x, y) 会立即执行 foo 并把返回值传给 goroutine,不是你想要的并发行为。常见错误是写成:
go process(item) // ❌ item 被立即求值,process 同步执行
正确做法是用匿名函数闭包捕获变量,确保参数在 goroutine 内部才被读取:
go func(item string) {
process(item)
}(item) // ✅ item 值被复制进闭包,goroutine 独立执行
- 循环中启动多个 goroutine 时,务必避免变量复用:用
for _, item := range items { go func(i string) { ... }(item) },而不是直接捕获循环变量item - 如果函数本身无参(如
func() { ... }),可直接go worker() - 闭包捕获的是值拷贝,对基本类型安全;但若传指针或 map/slice,仍需注意数据竞争
函数返回值无法直接从 goroutine 获取
goroutine 是异步的,go f() 不返回任何值,也不能用 result := go f()。想拿结果,必须显式通信:
- 用
chan配合return:让函数自己往 channel 发结果,主 goroutine 从 channel 收 - 用
sync.WaitGroup等待完成,但不传递结果 - 用
errgroup.Group(需引入golang.org/x/sync/errgroup)统一收集 error 和结果
典型模式:
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
ch := make(chan int, 1)
go func() {
ch <h3>带 defer 的函数在 goroutine 中行为容易误解</h3><p><code>defer</code> 在 goroutine 函数返回时触发,不是在主 goroutine 返回时。这意味着:</p>
- 如果 goroutine 函数里有
defer unlock(),它只在该 goroutine 结束时释放锁,不影响其他 goroutine - 若 defer 依赖外部变量(如闭包外的
file),要确认该变量生命周期是否覆盖到 defer 执行时刻 - 日志、资源清理类逻辑放 defer 没问题;但别指望 defer 能“同步阻塞主流程”
函数内启动 goroutine 要警惕栈增长和泄漏
一个函数调用后启动 goroutine,不代表该函数栈能立刻回收——只要 goroutine 还活着且闭包引用了局部变量,相关内存就无法 GC。
- 避免在短生命周期函数中启动长生命周期 goroutine 并捕获大对象(如整个 struct、大 slice)
- 用
runtime.Stack或 pprof 可查 goroutine 数量,防止意外堆积(比如忘记range ch退出条件) - HTTP handler 中启动 goroutine 时,务必检查请求上下文是否已 cancel:
if ctx.Err() != nil { return },否则可能跑出 request 生命周期
并发不是加个 go 就完事,关键在数据归属、生命周期和通信时机——漏掉任一环,轻则结果错乱,重则内存持续上涨。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










