Golang让协程交替输出

2019独角兽企业重金招聘Python工程师标准>>> Golang让协程交替输出
文章图片

What you are wasting today is tomorrow for those who died yesterday; what you hate now is the future you can not go back.
你所浪费的今天是昨天死去的人奢望的明天; 你所厌恶的现在是未来的你回不去的曾经。
之前用Golang写过一篇关于下载的文章(https://my.oschina.net/90design/blog/1607131), 最后提到:如果新需求是同时下载,并且按循序下载,最近看到在论坛里有人又再问起,就想起来更新一下此问题。
开始
  1. 两个协程交替输出1-20
package mainimport "fmt"func main() { A := make(chan bool, 1) B := make(chan bool) Exit := make(chan bool) go func() { for i := 1; i <= 20; i++ { if ok := <-A; ok { fmt.Println("A = ", 2*i-1) B <- true } } }() go func() { defer func() { close(Exit) }() for i := 1; i <= 20; i++ { if ok := <-B; ok { fmt.Println("B : ", 2*i) A <- true } } }() A <- true <-Exit }

解释:
【Golang让协程交替输出】首先给通道A一个缓存,并在主进程中发送数据,使其堵塞,在第一个Goroutine中通道A接收并开始执行, 此时B是堵塞等待的, 等A执行完成发送数据到通道B, B开始执行。

扩展写法 不过在论坛看到其他人码友直接使用一个输出通道搞定,贴出来大家看看:
func main() { ch := make(chan int) exit := make(chan struct{})go func() { for i := 1; i <= 20; i++ { println("g1:", <-ch)// 执行步骤1, 执行步骤5 i++//执行步骤6 ch <- i // 执行步骤7 } }()go func() { defer func() { close(ch) close(exit) }() for i := 0; i < 20; i++ { i++// 执行步骤2 ch <- i//执行步骤3 println("g2:", <-ch) //执行步骤4 } }()<-exit }


问题延伸 问题延伸出来, 如果 >2 个协程呢?
多个协程,按一定顺序执行任务
package mainimport ( "fmt" "io" )var ( num int A= make(chan int) B= make(chan int) C= make(chan int) D= make(chan int) exit = make(chan bool) )func main() { // 开启多协程 go Aa() go Bb() go Cc() go Dd() // 接收要输出的最大数 fmt.Println("输入要输出的最大数值:") _, ok := fmt.Scanf("%d\n", &num) if ok == io.EOF{ return } // 触发协程同步执行 A <- 1 // 执行结束 if <-exit{ return } } func Aa() { for { if count := <-A; count <= num { fmt.Println("A -> ", count) count++ B <- count }else{ fmt.Println("在通道D执行完成") close(exit) return } } } func Bb() { for { if count := <-B; count <= num { fmt.Println("B -> ", count) count++ C <- count }else{ fmt.Println("在通道A执行完成") close(exit) return } } } func Cc() { for { if count := <-C; count <= num { fmt.Println("C -> ", count) count++ D <- count }else{ fmt.Println("在通道B执行完成") close(exit) return } } }func Dd() { for { if count, ok := <-D; ok && count <= num { fmt.Println("D -> ", count) count++ A <- count }else{ fmt.Println("在通道C执行完成") close(exit) return } } }


解释:
以上代码通过多个协程建立多个方法的方式完成多协程的执行任务, 你可能会问了: 如果100个协程还要有100个对应的方法? 答案是: 肯定 , 不可能啊, 立马来个优化方案。
优化方案:
package mainimport ( "fmt" "io" "strconv" )var ( numint // 要输出的最大值 line= 0 // 通道发送计数器 exit= make(chan bool) chans []chan int // 要初始化的协程数量 )func main() { // 开启4个协程 chans = []chan int{ make(chan int), make(chan int), make(chan int), make(chan int) } // 多协程启动入口 go ChanWork(chans[0]) // 接收要输出的最大数 fmt.Println("输入要输出的最大数值:") _, ok := fmt.Scanf("%d\n", &num) if ok == io.EOF { return } // 触发协程同步执行 chans[0] <- 1 // 执行结束 if <-exit { return } } func ChanWork(c chan int) { // 协程数 lens := len(chans) for { // count为输出计数器 if count := <-chans[line]; count <= num { fmt.Println("channel "+strconv.Itoa(line)+" -> ", count) count++// 下一个发送通道 line++ if line >= lens { line = 0 //循环,防止索引越界 } go ChanWork(chans[line]) chans[line] <- count} else { // 通道编号问题处理 id := 0 if line == 0{ id = lens-1 }else{ id = line-1 } fmt.Println("在通道" + strconv.Itoa(id) + "执行完成") close(exit) return } } }

执行的结果:
Golang让协程交替输出
文章图片

解释:
可以说是通过递归的方式,通过一个协程执行完成再来生成第二个协程的方式, 本人是通过轮训channels的slice来完成协程之间的同步任务, 如果还有更好的方式请留言哦。 就算你有100W个也不惧怕, 看你配置了。

结语 就写到这里吧,详细的优化后期再完成, 细嚼才能慢咽啊!
建议很重要,交流进步才是硬道理啊!




转载于:https://my.oschina.net/90design/blog/1609453

    推荐阅读