错误在于循环变量被所有goroutine共享,导致读取到同一内存地址的最终值;正确做法是每次迭代创建局部副本或以参数形式传入当前值。

Go 语言里没有“异步函数”这个语法概念,go 关键字启动的是 goroutine,它本质是并发执行单元,不是 JS 那种带状态机的 Promise 异步函数。直接用 go func() {}() 就能跑,但错在哪儿、怎么优化,得看场景。
用 go 启动 goroutine 时为什么结果总不对
最常见问题是循环变量被所有 goroutine 共享,导致全部读到同一个值:
- 错误写法:
for i := 0; i → 输出可能是 <code>3 3 3 - 根本原因:匿名函数捕获的是变量
i的地址,不是当前值;等 goroutine 真正执行时,循环早已结束,i == 3 - 正确做法:显式传参,让每个 goroutine 拿到自己的副本:
go func(val int) { fmt.Println(val) }(i) - 额外注意:如果传的是指针或 map/slice,仍可能共享底层数据,需深拷贝或只传不可变值
make(chan T, 1) 是最安全的 Future 模式
需要“启动后稍后取结果”,别用无缓冲 channel,也别手动 close() —— 容易卡死或 panic:
- 无缓冲
chan string:goroutine 还没启动完,主线程就,会永久阻塞 - 带缓冲
make(chan Result, 1):写入立刻返回,不依赖 goroutine 执行进度 - 接收方用
val, ok := 判断是否送达,<code>ok == false表示 channel 已关闭且无数据(但这里不关更稳妥) - 结构体建议含
Err error字段,避免用nil做成功标志,统一错误路径
多个任务要等齐?优先选 sync.WaitGroup 而不是 range ch
当你要确保 N 个 goroutine 全部完成(不关心结果,只关心“做完”),WaitGroup 更轻量、语义更清晰:
-
range ch要求你主动close(ch),且必须在所有发送完成后调用,时机错一点就死锁 -
WaitGroup只需wg.Add(N)+ 每个 goroutine 结尾wg.Done()+ 主协程wg.Wait() - 注意:
wg.Add()必须在go之前调用,否则竞态;defer wg.Done()放在 goroutine 函数开头最稳 - 若还需结果,可组合使用:
WaitGroup控制生命周期,chan收集数据
真正容易被忽略的点是:goroutine 启动后,错误处理和资源清理常被跳过。比如 HTTP 请求没设超时、文件句柄没 Close()、数据库连接没归还——这些不会影响“启动”,但会让程序在高并发下悄无声息地崩掉。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











