用channel实现协程交替打印需避免sleep,应使用两个无缓冲channel(c1、c2)作为同步信号:主goroutine先向c1发初始信号,a打印后发信号到c2,b收到后打印再发信号回c1,循环往复确保严格交替。

Go 里用 channel 实现协程交替打印,别用 sleep 控制顺序
用 time.Sleep 强行错开两个 goroutine 的执行时机是典型误区——它不可靠、不精确,且在高负载或 GC 暂停时直接失效。真正可控的交替必须依赖同步原语,channel 是最自然的选择:一个 goroutine 发送完数字后,等另一个 goroutine 消费完再继续。
- 用两个 unbuffered channel(
c1和c2)做“接力棒”:goroutine A 打印后向c2发一个信号,表示“我干完了,轮到你”;goroutine B 收到后才开始打印,完后再发信号回c1 - 主 goroutine 先向
c1发一个初始信号,触发第一个打印 - 数字范围用闭包变量或传参控制,避免闭包捕获循环变量导致全打 100
func main() {
c1 := make(chan struct{})
c2 := make(chan struct{})
go func() {
for i := 1; i <h3>为什么不能只用一个 channel?</h3><p>单 channel(比如只用 <code>c</code>)无法区分“该谁打了”。如果两个 goroutine 都在读同一个 channel,调度器可能让偶数协程抢到前几次信号,导致输出乱序(如 2,4,1,6…)。更糟的是,当数字打完,最后一个 goroutine 可能还在阻塞读 channel,造成死锁。</p>
- 单 channel + 布尔标志位?不行——跨 goroutine 读写非原子,需额外加锁,失去 channel 的简洁性
- buffered channel 设成 1?本质还是单向信号,没解决“谁该响应”的判定问题
- 正确解法必须有方向性:A → B → A → B,靠两个 channel 构成闭环信号流
实际面试中容易被追问的点
面试官常从边界和鲁棒性切入:
- 如果把 100 改成 101,奇数协程多打一个,偶数协程少一次接收,
c2最后一次发送会阻塞——得让两个 goroutine 在各自循环末尾判断是否已到上限,不发信号 - 不用
fmt.Println而用fmt.Print加换行?注意竞态:输出缓冲不同步可能导致数字粘连(如 “123”),但题目只要求“打印”,通常不扣分 - 能否用
sync.Mutex替代 channel?可以,但要手动维护状态变量(如turn = 0/1),代码更长,且 lock/unlock 易漏写;channel 天然带同步语义,更符合 Go 的惯用法
Python asyncio 版本要注意 event loop 生命周期
Python 里用 asyncio 写类似逻辑,常见错误是忘记 await 或用错 asyncio.Event:
- 不能用
time.sleep(),必须用await asyncio.sleep(0)让出控制权 - 两个 task 要共享同一个
asyncio.Event实例,靠set()/clear()切换状态;或者用asyncio.Queue(maxsize=1)模拟 channel - 主函数必须用
asyncio.run(main())启动,否则 event loop 未创建,await报RuntimeError: no running event loop
关键不是语法对不对,而是理解:交替的本质是协作式调度,不是时间切片。无论语言,信号传递路径必须清晰、无歧义、无竞态。
channel 的配对使用、初始触发、以及循环结束时的信号收口,这三处最容易漏处理。










