Go中无通用方案保证goroutine顺序执行;WaitGroup仅等待全部完成而不控制内部时序;需链式依赖时应使用channel传递数据或状态。
Go 里没有“保证 goroutine 顺序执行”的通用方案——因为并发的本质就是不承诺执行时序。所谓“顺序控制”,实际是通过同步原语让某些操作**按逻辑顺序发生**,而非让 goroutine 按启动顺序串行跑。
sync.WaitGroup 等待所有 goroutine 完成,但不控制内部顺序这是最常见误解:以为 Wa 能让 goroutines 按 
go f() 的调用顺序执行。它只保证主 goroutine 等待全部结束,不干预调度。
典型误用场景:循环启动多个 goroutine 处理任务,期望输出按 0,1,2… 顺序打印,结果乱序。
WaitGroup 适合“等全部做完再继续”,不适合“让第2个等第1个做完再开始”WaitGroup 去模拟串行会掩盖真正的并发需求channel 实现严格先后依赖(如 A 完成后才触发 B)channel 是 Go 中最自然的顺序协调工具,尤其适合“一个阶段输出是下一个阶段输入”的场景。
ch := make(chan int, 1)
go func() {
result := doStepA()
ch <- result // A 完成后发信号
}()
go func() {
a := <-ch // B 必须等 A 发送后才继续
doStepB(a)
}()关键点:
make(chan int))天然阻塞,发送和接收必须配对,强制时序make(chan int, 1))可解耦快慢,但缓冲区满时仍阻塞,逻辑上仍是“先发后收”,否则可能死锁;建议配合 select + default 或超时
sync.Mutex 或 sync.RWMutex 保护共享状态的修改顺序当多个 goroutine 需按特定顺序更新同一变量(如累加计数、构建链表),靠锁保证临界区互斥,间接实现操作顺序性。
例如:按索引顺序写入切片,防止竞态导致覆盖或错位:
var mu sync.Mutex
data := make([]int, 10)
for i := 0; i < 10; i++ {
go func(idx int) {
mu.Lock()
data[idx] = compute(idx)
mu.Unlock()
}(i)
}注意:
sync.Cond
RWMutex,避免读操作互相阻塞time.Sleep 或轮询模拟顺序这是初学者常见陷阱:在 goroutine 里加 time.Sleep(10 * time.Millisecond) 试图“错开执行”。它既不可靠(调度延迟不确定),又难维护(时间值凭经验),还拖慢整体性能。
真实问题往往出在设计层面:
真正难的不是写出顺序代码,而是判断哪部分逻辑**必须顺序**、哪部分其实可以并行——这需要从数据流和状态变更出发,而不是盯着 goroutine 启动那一行。