分析:
顺序:需要同步,无 buffer channel 正好可以实现;
无限:主协程阻塞,子协程为 deamon,或者说无限循环;
需要开3个线程(协程),
- 一个线程用来输出1,无限输出
- 一个线程用来输出2,无限输出
- 一个线程用来输出3,无限输出
实现无限输出 123123123...
package main
import (
"fmt"
"time"
)
func print(curCh chan struct{}, nextCh chan struct{}, idx int64) {
for{
curCh<- struct{}{} // 3
fmt.Println(idx + 1)
time.Sleep(time.Second) // 方便人来观察,放慢打印输出的速度
<-nextCh // 4
}
}
func main() {
chs := []chan struct{}{make(chan struct{}), make(chan struct{}), make(chan struct{})} // 5
for i := 0; i < len(chs); i++ { // 7
go print(chs[i], chs[(i + 1) % len(chs)], int64(i)) // 6
}
<-chs[0] // 2
select{} // 1
}
上面的代码里边,可以说是每行代码都有用处,一行都不能少。实现上也可以换一种同步的方式,变换下 channel 的接收和发送状态,有兴趣自己修改下即可。
先有个大概的理解,
<-chs[0],这是注释 5 中定义的 chan 数组的第一个元素,想象一下接力赛,一个队有 n 个人,n 个队友在固定位置准备就绪(这里起了3个无限循环的子协程),阻塞(等待)在那里(注释3),发令枪响起,第一个队友拿到令牌开始起跑(注释2),其他选手处于阻塞状态,等待前一个队友的接力,前一个队友完成了交接给下一个队友(注释4),下一个选手开始起跑。一直到最后一个队友跑完,是为输出一遍。假设接力赛无限循环,就能达到无限循环输出的状态。
看下输出结果,脑补下满屏的123...
注释 1:
select{} 语句,用于阻塞主协程,如果要实现无限执行子协程,主协程必须阻塞,而且在主协程阻塞时,必须要有活动的子协程,否则会报 deadlock。
做下实验,不阻塞主协程,主协程退出,子协程也不会打印输出,
把 print 函数中的 for 语句注释掉执行,只能输出一遍,继续往下执行时由于没有活着的子协程了,select 报 deadlock。加上无限循环可以保证子协程一直处于活动状态。
注释 2:
<-chs[0],发令枪响起,第一个队友拿到令牌开始起跑,其他选手处于阻塞状态,等待前一个队友的接力。
注释3:
curCh<- struct{}{},接力成功,解除阻塞状态,开始起跑。
注释4:
<-nextCh,接力给下个队友...循环
注释6和7:
起 n 个无限循环的协程,就是给接力赛征召 n 个队员,这里n=3。取模的方式可以达到环形状态。
Have Fun