
go 程序中,若主 goroutine(main 函数)提前退出,其他启动的 goroutine 会被强制终止;使用 sync.waitgroup 是最惯用、可靠且可扩展的解决方案,通过计数协调确保主线程等待所有子任务完成。
go 程序中,若主 goroutine(main 函数)提前退出,其他启动的 goroutine 会被强制终止;使用 sync.waitgroup 是最惯用、可靠且可扩展的解决方案,通过计数协调确保主线程等待所有子任务完成。
在 Go 并发编程中,main() 函数的返回即代表整个程序退出——此时所有尚未完成的 goroutine 会被立即终止,无论它们是否已开始执行。这正是原示例中 printElo() 和 printHello() 输出完全缺失的根本原因:main 在启动 20 个 goroutine 后立刻结束,系统无任何等待机制。
解决该问题的惯用且推荐方式是 sync.WaitGroup。它提供轻量级的计数器语义,无需通道或锁,专为“等待一组 goroutine 完成”这一场景设计。
以下为修正后的完整代码:
package main
import (
"fmt"
"sync"
)
var wg = &sync.WaitGroup{}
func printElo() {
defer wg.Done() // 确保无论函数如何退出(正常/panic),计数器都减一
fmt.Printf("Elo\n")
}
func printHello() {
defer wg.Done()
fmt.Printf("Hello\n")
}
func main() {
fmt.Printf("This will print.")
i := 0
for i <p>✅ <strong>关键实践原则(务必遵守):</strong> </p>
-
wg.Add(n)必须在go语句之前调用,且在同一 goroutine(通常是 main)中执行,否则存在竞态风险; -
wg.Done()应始终通过defer调用,以保障 panic 时仍能正确释放计数; -
WaitGroup是值类型(struct),若需跨函数传递,*必须传 `sync.WaitGroup` 指针**,否则副本操作将对原实例无效; - 不要重复调用
wg.Add()后未匹配Done(),或调用Done()超出初始计数,这会导致 panic 或死锁。
⚠️ 补充说明:虽然也可用 time.Sleep “凑巧”等待(如 time.Sleep(100 * time.Millisecond)),但这是反模式——既不可靠(无法保证所有 goroutine 已调度并完成),也不符合 Go 的并发哲学。WaitGroup 提供的是确定性同步,是生产环境唯一推荐方案。
总结:sync.WaitGroup 是 Go 中协调 goroutine 生命周期的基石工具。掌握其 Add / Done / Wait 三要素的正确时序与作用域,是写出健壮并发程序的第一步。










