必须配合同步机制才能可靠运行goroutine;常用sync.WaitGroup显式等待,避免主协程提前退出导致任务丢失或panic。

Go 语言里用 go 关键字启动 goroutine 是最轻量的并发方式,但直接裸写 go f() 极大概率导致主协程提前退出、任务丢失或 panic —— 必须配合同步机制才能可靠运行。
goroutine 启动后立刻返回,主协程不等它就结束了
这是新手最常踩的坑:写完 go doWork() 就以为任务在后台跑完了,结果程序一闪而过,什么都没输出。因为 main 协程执行完就退出,所有 goroutine 被强制终止。
- 用
sync.WaitGroup显式等待:先wg.Add(1),goroutine 结束时调wg.Done(),main 里调wg.Wait() - 别在循环里直接传循环变量(如
for i := 0; i ),会全打印3;改用闭包参数传值:go func(n int) { fmt.Println(n) }(i) - 避免用
time.Sleep等待 —— 不可靠、难维护、掩盖真实同步问题
多个 goroutine 共享变量时数据竞争(data race)
当多个 goroutine 同时读写同一个变量(比如计数器 count++),结果不可预测。Go 编译器无法自动加锁,必须手动处理。
- 优先用
sync.Mutex或sync.RWMutex:临界区前后加mu.Lock()/mu.Unlock() - 对简单原子操作(如 int64、uint32、指针赋值),用
sync/atomic包,比锁更快,例如atomic.AddInt64(&count, 1) - 编译时加
-race标志检测竞争:go run -race main.go,上线前务必跑一次
goroutine 泄漏:忘记关闭 channel 或未消费完消息
用 channel 做 goroutine 间通信很常见,但一旦发送端发完不关 channel,或接收端没读完,goroutine 就永远卡在 ch 或 上,内存持续增长。
- 发送方完成时记得
close(ch);接收方用for v := range ch自动退出,而不是死循环for { - 带缓冲的 channel(
make(chan int, 10))能缓解阻塞,但不能替代正确关闭逻辑 - 如果 goroutine 内部有网络请求或定时器,确保它们都有超时控制(如
context.WithTimeout),否则可能永久挂起
真正难的不是启动 goroutine,而是定义清楚谁负责结束、谁负责清理、错误怎么传递。一个没回收的 goroutine、一个没关闭的 channel、一个没处理的 panic,都可能让服务在线上缓慢退化 —— 这些细节不会报错,但会在高负载时集中爆发。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











