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-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个也不惧怕, 看你配置了。
结语
就写到这里吧,详细的优化后期再完成, 细嚼才能慢咽啊!
建议很重要,交流进步才是硬道理啊!