go 后必须跟函数调用表达式,如 go worker() 或 go func(){}();main 退出会强制终止所有 goroutine,应使用 sync.waitgroup 显式等待。

go 后面必须跟函数调用,不是函数名
写 go worker 会编译报错,Go 不允许只传函数值;必须是 go worker() 或 go func(){}() 这类带括号的调用表达式。这是唯一合法语法,没有例外。
- 常见错误现象:
./main.go:10:3: cannot use worker (type func(int)) as type func() value in argument to go - 匿名函数也一样:要
go func() { ... }(),不能漏最后那对括号 - 返回值被静默丢弃——哪怕
func() int返回了数字,你也拿不到;需要结果就改用chan或指针传参
main 退出 = 所有 goroutine 强制终止
没等 goroutine 跑完,程序就结束了,这是最常遇到的“goroutine 没输出”问题。根本原因不是 goroutine 慢,而是 main 函数跑太快、直接退出了。
- 别用
time.Sleep(1 * time.Second)猜时长——它不可靠,上线后可能失效 - 正确做法:用
sync.WaitGroup显式等待,wg.Add(1)必须在go前,wg.Done()放在 goroutine 内部结尾(推荐defer) - 循环启动多个时,注意闭包捕获变量:写
for i := 0; i 很可能输出 <code>3 3 3;应改为go func(ii int) { fmt.Println(ii) }(i)
goroutine 泄漏比 panic 更难发现
泄漏不会立刻崩溃,但内存缓慢上涨、runtime.NumGoroutine() 持续不降,pprof 查看堆栈里一堆卡在 channel 操作上——说明它们永远等不到数据。
- 典型场景:往无缓冲
chan发送数据,但没人接收;或从空chan接收,但没人发送 - HTTP handler 里启 goroutine 却没监听
req.Context().Done(),请求取消后 goroutine 还在跑 - 轮询任务(如
time.Ticker)忘了ticker.Stop(),导致 goroutine 和 timer 双重泄漏 - 调试建议:加
GODEBUG=gctrace=1观察 GC 频次;用curl http://localhost:6060/debug/pprof/goroutine?debug=2看实时堆栈
别无脑开 goroutine,worker pool 更可控
并发量大时,每来一个请求就 go handle(req),很容易把内存和调度器压垮。10 万个活跃 goroutine 不是设计目标,而是事故征兆。
- 真实服务场景(如 API 网关、批量图片处理)该用固定数量的 worker + task channel
- 核心是限制并发数:用
make(chan func(), N)缓冲提交压力,worker 用for task := range taskChan阻塞接收 - 关闭 pool 时先
close(taskChan),再wg.Wait();顺序反了 worker 会卡死 - 若需超时或取消,让每个
task自带context.Context,而不是在 Submit 时硬塞一个已 cancel 的 ctx
事情说清了就结束。真正难的从来不是怎么起一个 goroutine,而是它什么时候停、怎么停得干净、谁负责清理资源。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











