Go通道同步能否保证goroutine打印顺序?代码同步性验证
关于Go代码同步性的确认与分析
你的代码如下:
func main() { ch1 := make(chan bool) done := make(chan bool) go func() { fmt.Println("Two") ch1 <- true }() go func() { fmt.Println("One") <-ch1 done <- true }() <-done }
核心问题解答
你的代码完全可能出现先输出"Two"再输出"One"的情况,顺序错乱是必然存在的概率性问题,之前20次运行符合预期只是巧合。
原因很直接:Go runtime对goroutine的调度顺序是无规则的,两个goroutine启动后,谁先被执行完全由调度器决定:
- 如果调度器先调度第一个goroutine:会先打印"Two",然后卡在
ch1 <- true(此时没有goroutine接收这个值),直到第二个goroutine被调度,打印"One"、接收ch1的值,最后触发done结束main - 如果调度器先调度第二个goroutine:会先打印"One",卡在
<-ch1,等第一个goroutine打印"Two"并发送值后,才会继续执行
修正方案
如果要确保严格的"One"→"Two"输出顺序,需要用channel明确控制执行流,让打印"Two"的goroutine等待打印"One"的信号:
func main() { ch := make(chan struct{}) done := make(chan bool) // 等待信号再打印Two go func() { <-ch fmt.Println("Two") done <- true }() // 先打印One,再发信号通知 go func() { fmt.Println("One") ch <- struct{}{} }() <-done }
如果场景更简单,甚至可以去掉多余的goroutine直接同步执行;如果必须用goroutine,也可以用WaitGroup实现更清晰的等待逻辑:
import "sync" func main() { var wg sync.WaitGroup wg.Add(1) go func() { defer wg.Done() fmt.Println("One") }() // 等One打印完再打印Two wg.Wait() fmt.Println("Two") }
最佳实践
- 非必要不要为了并行而强行拆分goroutine,简单顺序逻辑直接同步执行更高效
- 用goroutine时必须明确同步依赖,通过channel或WaitGroup严格约束执行顺序
- WaitGroup适合批量等待多个goroutine完成的场景,单依赖的简单场景用channel更轻量
内容的提问来源于stack exchange,提问作者Swapnil Mishra
相关产品推荐
相关产品推荐

