Go 程序中启动 Goroutine 后,若主 goroutine 过早退出,其他 goroutine 将被强制终止;仅靠 time.Sleep 等待不可靠,应使用通道(channel)或 sync.WaitGroup 实现精确同步。
go 程序中启动 goroutine 后,若主 goroutine 过早退出,其他 goroutine 将被强制终止;仅靠 `time.sleep` 等待不可靠,应使用通道(channel)或 `sync.waitgroup` 实现精确同步。
在 Go 中,go 关键字用于启动一个新的 goroutine,它会在后台并发执行,而主 goroutine(即 main 函数)不会自动等待其他 goroutine 完成。一旦 main 函数执行完毕,整个程序立即终止——无论其他 goroutine 是否仍在运行、是否已打印输出。这正是你遇到“无输出”问题的根本原因。
你代码中两次调用 time.Sleep(1000) 并非必需,而是因为单次 1s 睡眠偶然不足以覆盖所有 goroutine 的调度与执行耗时(尤其在高负载或低性能环境中)。即使 time.Sleep(2000) 也可能失败——因为 Sleep 是基于时间的粗粒度等待,不具备同步语义,无法保证 goroutine 已真正执行完毕。
✅ 正确做法:使用显式同步机制,推荐两种主流方案:
方案一:使用 sync.WaitGroup(推荐用于固定数量的 goroutine)
package main
import (
"fmt"
"sync"
)
func doSomething(integer int, wg *sync.WaitGroup) {
defer wg.Done() // 标记该 goroutine 完成
fmt.Println(integer)
}
func main() {
var wg sync.WaitGroup
for i := 1; i <h3>方案二:使用 channel(适合需传递结果或响应的场景)</h3><pre class="brush:php;toolbar:false;">package main
import "fmt"
func doSomething(integer int, done chan<p>⚠️ 注意事项:</p>
- 不要依赖 time.Sleep 做同步:它不可靠、不精确,且掩盖了并发设计缺陷;
- 避免变量捕获陷阱:原代码中 i 在循环中被所有 goroutine 共享,可能导致重复打印 10(因循环结束时 i==11,而 goroutine 异步读取)。修正方式是传参(如 go doSomething(i))或在循环内定义局部变量;
- WaitGroup 必须在 goroutine 内调用 Done(),且 Add() 应在 go 之前调用,否则存在竞态风险。
总结:Goroutine 的生命周期独立于主函数,但程序退出由主 goroutine 控制。要安全观察并发行为,必须主动同步——sync.WaitGroup 是最直观、高效的选择;channel 则适用于更复杂的通信需求。掌握这些原语,才能写出健壮、可预测的 Go 并发程序。











