分享
  1. 首页
  2. 文章

Golang让协程交替输出

90design · · 5140 次点击 · · 开始浏览
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

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 main
import "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
}

解释:

首先给通道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 main
import (
	"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 main
import (
	"fmt"
	"io"
	"strconv"
)
var (
	num int // 要输出的最大值
	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
		}
	}
}

执行的结果:

解释:

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

结语

就写到这里吧,详细的优化后期再完成, 细嚼才能慢咽啊!

建议很重要,交流进步才是硬道理啊!


有疑问加站长微信联系(非本文作者)

本文来自:开源中国博客

感谢作者:90design

查看原文:Golang让协程交替输出

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

关注微信
5140 次点击
1 回复 | 直到 2021年03月12日 10:29:43
暂无回复
添加一条新回复 (您需要 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏