
Go程序主函数退出后会立即终止所有goroutine,导致未完成的并发任务(如fmt.Println)被静默丢弃;必须通过同步机制(如sync.WaitGroup)等待goroutine完成。
go程序主函数退出后会立即终止所有goroutine,导致未完成的并发任务(如`fmt.println`)被静默丢弃;必须通过同步机制(如`sync.waitgroup`)等待goroutine完成。
在Go中,启动一个goroutine(如 go func() {...}())是非阻塞且异步的:它将任务提交到Go运行时调度器后,主goroutine(即main函数)会继续执行后续代码,并在返回时直接退出整个程序——此时所有尚未执行完毕的子goroutine会被强制终止,其内部的I/O操作(包括fmt.Println)根本来不及触发。
你提供的代码正是典型场景:
package main
import "fmt"
func main() {
for i := 0; i < 20; i++ {
go func(j int) {
fmt.Println(j + 100)
}(i)
}
// ⚠️ 主函数在此结束 → 程序立即退出,goroutines被丢弃
}该循环虽启动了20个goroutine,但main函数几乎瞬间执行完毕,没有为它们预留任何运行时间,因此控制台无任何输出。
✅ 正确做法是引入同步原语确保主goroutine等待所有工作完成。最常用、最推荐的方式是 sync.WaitGroup:
立即学习“go语言免费学习笔记(深入)”;
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
for i := 0; i < 20; i++ {
wg.Add(1) // 每启动一个goroutine,计数器+1
go func(j int) {
defer wg.Done() // goroutine结束时计数器-1
fmt.Println(j + 100)
}(i)
}
fmt.Println("Waiting...")
wg.Wait() // 阻塞直到计数器归零(即所有goroutine完成)
fmt.Println("Done.")
}? 关键要点:
-
wg.Add(1)必须在go语句之前调用,避免竞态(因Add和Done需严格配对); - 使用
defer wg.Done()是惯用写法,确保即使goroutine内发生panic也能正确减计数; - 不要依赖
time.Sleep替代同步——它不可靠、不精确,且掩盖了并发控制的本质问题; - 若需传递循环变量,请像示例中一样通过参数
j int显式捕获当前值,避免闭包引用同一变量i导致全部打印相同结果(本例已正确处理)。
掌握 WaitGroup 是编写健壮Go并发程序的第一课:它让“启动并发”与“等待完成”解耦,既保持简洁性,又保证确定性行为。


















