
go 程序在 `main` 函数返回后立即终止,**不会等待其他 goroutine 完成**;即使添加 `time.sleep`,若主协程提前退出或同步缺失,子 goroutine 仍可能被强制中断。
在 Go 中,main 函数的生命周期即整个程序的生命周期。一旦 main 函数执行完毕(无论是否包含 time.Sleep),运行时会立即终止所有正在运行的 goroutine —— 包括已启动但尚未完成的 my_func。这正是你观察到“加了 time.Sleep 反而没输出”的根本原因:time.Sleep(time.Second) 延迟了向 channel 发送值的时间,但并未阻止 main 在发送后立刻结束;而 fmt.Println(
关键误区在于:time.Sleep 并不提供同步保证,它只是让 main 协程暂停一段时间,但无法确保其他 goroutine 已执行完毕。Go 的并发模型强调显式同步,而非依赖时间延迟。
✅ 正确做法:使用 channel 实现 goroutine 同步
最简洁可靠的方式是引入一个“完成信号” channel,在子 goroutine 完成工作后通知 main:
package main
import (
"fmt"
"time"
)
func my_func(c, done chan int) {
val := <-c
fmt.Println(val)
done <- 1 // 发送完成信号
}
func main() {
c := make(chan int)
done := make(chan int) // 无缓冲 channel,用于同步
go my_func(c, done)
time.Sleep(time.Second) // 模拟延迟(非必需,仅为复现原场景)
c <- 3
<-done // 阻塞等待子 goroutine 完成 —— 关键同步点!
}✅ 输出:3(稳定可重现)
? 为什么有效?
⚠️ 注意事项与最佳实践
- ❌ 避免用 time.Sleep 替代同步:它不可靠、平台依赖、易受调度延迟影响,且掩盖了并发逻辑缺陷;
- ✅ 优先使用 channel、sync.WaitGroup 或 context 进行显式协调;
- ? 对于单次任务,无缓冲 channel(如本例)语义清晰;若需多次通知,可考虑带缓冲 channel 或 sync.WaitGroup.Add(1)/Done();
- ? main 函数应主动等待关键 goroutine 完成,这是 Go 并发编程的基本契约。
总结:Go 不会为“后台任务”续命——你必须亲手拉住 main,直到所有重要工作真正落地。同步不是可选项,而是并发安全的起点。










