
当 main 函数返回时,go 程序立即退出,不会等待其他 goroutine 完成;因此若未显式同步,含 time.sleep 的代码可能因 main 提前结束而无法输出结果。
在 Go 中,程序生命周期由 main 函数严格控制:main() 函数执行完毕(即 main 函数体最后一行代码执行后),整个程序立即终止——无论其他 goroutine 是否仍在运行、是否已开始执行或是否即将打印日志。这正是你观察到“添加 time.Sleep(time.Second) 后反而不输出”的根本原因。
表面上看,time.Sleep 延迟了 c
✅ 正确做法:使用通道实现 goroutine 同步
最符合 Go 风格的解决方案是引入一个 done 通道,让 main 主动等待工作 goroutine 完成关键操作(如打印)后再退出:
package main
import (
"fmt"
"time"
)
func my_func(c, done chan int) {
val := <-c
fmt.Println(val) // 关键逻辑执行完成
done <- 1 // 通知 main:我已就绪
}
func main() {
c := make(chan int)
done := make(chan int) // 无缓冲通道,保证同步语义
go my_func(c, done)
time.Sleep(time.Second) // 模拟延迟(非必需,仅用于演示场景)
c <- 3
<-done // 阻塞等待 my_func 发送信号 → 确保打印已完成
}? 关键点解析:done 是无缓冲通道,
⚠️ 注意事项与最佳实践
- ❌ 避免用 time.Sleep 作为 goroutine 同步手段:它不可靠、难以维护,且在高负载或不同环境中行为不一致;
- ✅ 优先使用通道(channel)、sync.WaitGroup 或 context 进行显式同步;
- ? 若 goroutine 仅需单次通知,无缓冲通道简洁高效;若需多次通知或计数,sync.WaitGroup 更合适;
- ? 在真实项目中,还应考虑超时控制(如 select + time.After),防止死锁:
select {
case <-done:
fmt.Println("goroutine completed successfully")
case <-time.After(2 * time.Second):
fmt.Println("timeout: goroutine did not finish in time")
}总之,Go 的并发模型强调明确的通信与同步,而非隐式的执行时序依赖。理解 main 的生命周期边界,并主动设计同步机制,是编写健壮并发程序的第一课。










