一、残酷真相:你不是 goroutine 的老板

想象一下:你雇了100个程序员(goroutine),告诉他们"去干活吧!",然后——

func main() {
    go fmt.Println("我是1号程序员")
    go fmt.Println("我是2号程序员")
    go fmt.Println("我是3号程序员")
    // ...等等,人呢?!
}

输出

(程序秒退,啥也没打印)

😱 发生了什么
主程序(main goroutine)就像个无情的HR:任务派发完,立刻下班跑路,根本不等其他 goroutine 干活![[23]]

💡 核心事实:Goroutine 由 Go runtime 调度,没有优先级概念,你无法命令"3号先干,5号等会儿" [[19]]。试图控制执行顺序?就像试图指挥一群喝咖啡的猫——它们有自己的想法 ☕🐱

二、三大"哄猫"技巧(实用同步方案)

技巧1️⃣:time.Sleep —— 最糙但有效的"等等我"

func main() {
    go fmt.Println("我是1号程序员")
    go fmt.Println("我是2号程序员")
    
    time.Sleep(100 * time.Millisecond) // HR:我再坐100毫秒...
}

原理:让主 goroutine 打个盹,给其他 goroutine 留出表演时间 [[20]]

缺点

  • 😴 睡多了浪费,睡少了翻车(“100毫秒够吗?200?500?”)
  • 🤡 像用闹钟管理团队:“大家10点开工!” —— 但有人9:59到,有人10:05才醒

适用场景:写 demo、测试时临时救急
生产环境:会被同事追着打(真的)


技巧2️⃣:sync.WaitGroup —— 点名签到制 ✅

这才是正经管理方式!让 goroutine 主动汇报:“老板,我干完啦!”

func main() {
    var wg sync.WaitGroup
    
    wg.Add(3) // 今天有3个程序员上班
    
    go func() {
        defer wg.Done() // 干完活自动打卡下班
        fmt.Println("1号:需求写完了!")
    }()
    
    go func() {
        defer wg.Done()
        fmt.Println("2号:代码写完了!")
    }()
    
    go func() {
        defer wg.Done()
        fmt.Println("3号:文档写完了!(才怪)")
    }()
    
    wg.Wait() // HR:等所有人都打卡下班,我才锁门!
    fmt.Println("✅ 全员下班,项目收工!")
}

输出(顺序随机,但保证全部执行完):

3号:文档写完了!(才怪)
1号:需求写完了!
2号:代码写完了!
✅ 全员下班,项目收工!

灵魂三件套

方法 作用 记忆口诀
Add(n) 登记上班人数 “点名:今天3人到岗”
Done() 打卡下班 “我干完了,溜了溜了”
Wait() 等全员下班 “最后一个走的关灯”

💡 WaitGroup 本质:一个带原子操作的计数器,线程安全地跟踪"还有几个活没干完" [[38]]


技巧3️⃣:Channel —— 传纸条式协作 📝

当 goroutine 需要按顺序交接工作时,channel 是最佳拍档:

func main() {
    ch1 := make(chan string)
    ch2 := make(chan string)
    
    // 第一棒:需求分析
    go func() {
        time.Sleep(100 * time.Millisecond)
        fmt.Println("🏃 1号:需求分析完成!")
        ch1 <- "需求文档" // 传纸条给下一位
    }()
    
    // 第二棒:开发实现
    go func() {
        doc := <-ch1 // 等纸条
        fmt.Printf("👩‍💻 2号:收到「%s」,开始 coding...\n", doc)
        time.Sleep(150 * time.Millisecond)
        ch2 <- "可运行代码"
    }()
    
    // 第三棒:测试验收
    result := <-ch2
    fmt.Printf("✅ 3号:收到「%s」,测试通过!上线!\n", result)
}

输出

🏃 1号:需求分析完成!
👩‍💻 2号:收到「需求文档」,开始 coding...
✅ 3号:收到「可运行代码」,测试通过!上线!

Channel 的魔力

  • 发送 <-ch:阻塞直到有人接收
  • 接收 ch<-:阻塞直到有人发送
  • 天然同步:不用手动 Sleep,纸条不到手绝不开工!

三、实战:让 goroutine 按"节拍器"跳舞 🎵

想让 goroutine 每隔固定时间干活?用 time.Ticker

func main() {
    ticker := time.NewTicker(500 * time.Millisecond)
    defer ticker.Stop()
    
    done := make(chan bool)
    
    go func() {
        for i := 1; i <= 5; i++ {
            <-ticker.C // 等"滴答"声
            fmt.Printf("💃 第 %d 拍:Goroutine 跳舞!\n", i)
        }
        done <- true
    }()
    
    <-done
    fmt.Println("🎉 舞蹈结束,完美谢幕!")
}

输出

💃 第 1 拍:Goroutine 跳舞!
💃 第 2 拍:Goroutine 跳舞!
...
🎉 舞蹈结束,完美谢幕!

⚠️ 注意time.Sleeptime.Ticker 都会让当前 goroutine 主动让出 CPU,runtime 会调度其他 goroutine 执行 [[20]] —— 这是触发调度的关键!

四、避坑指南:goroutine 的"死亡陷阱" 💀

❌ 陷阱1:无限循环不吃"调度点"

// 危险!这个 goroutine 会霸占 CPU,永不交出控制权
go func() {
    for {
        // 纯计算,无 channel / sleep / syscall
        // Go 1.14+ 有抢占式调度,但依然不推荐!
    }
}()

解法:每轮循环加个"喘气点"

for {
    // ...干活...
    runtime.Gosched() // 主动让出调度权
    // 或者
    time.Sleep(1 * time.Millisecond)
}

❌ 陷阱2:闭包变量陷阱

// 错误示范:所有 goroutine 都打印 5!
for i := 0; i < 5; i++ {
    go func() {
        fmt.Println(i) // i 已变成 5
    }()
}
time.Sleep(100 * time.Millisecond)

正确姿势

// 方案1:传参
for i := 0; i < 5; i++ {
    go func(id int) {
        fmt.Println(id)
    }(i) // 立即传入当前i值
}

// 方案2:循环内定义新变量(Go 1.22+ 自动修复)
for i := 0; i < 5; i++ {
    i := i // 复制一份
    go func() {
        fmt.Println(i)
    }()
}

五、终极心法:接受"不确定性" 🌊

“Goroutine 的魅力,不在于精确控制,而在于优雅地拥抱不确定性。”

你想控制… 现实 正确姿势
执行顺序 ❌ 不可能 用 channel 传递依赖
执行时间 ❌ 不精确 用 ticker 定时触发
CPU 占用 ⚠️ 有限控制 GOMAXPROCS 限制 P 数量
完成等待 ✅ 可以 用 WaitGroup / channel

六、彩蛋:一行代码看透本质

go func() { fmt.Println("我是自由的!") }()
// 翻译:我把任务扔给 runtime,然后——
//      "老天爷(scheduler)安排吧!🙏"

结语:做 goroutine 的"园丁",而非"驯兽师"

  • 🌱 园丁思维:提供土壤(channel)、阳光(CPU)、水分(调度点),让 goroutine 自然生长
  • 🦁 驯兽师思维:挥舞鞭子"你必须现在执行!" —— 结果:被 goroutine 反噬 😼

记住:Go 的并发哲学是"通信共享内存",而非"锁住一切"。当你学会用 channel 优雅地传递消息,用 WaitGroup 从容地等待完成,你会发现——那些看似"不听话"的 goroutine,其实比猫好哄多了 🐱➡️😺

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐