這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
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 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}
解釋:
首先給通道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 intA = 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 (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 := 0if line == 0{id = lens-1}else{id = line-1}fmt.Println("在通道" + strconv.Itoa(id) + "執行完成")close(exit)return}}}
執行的結果:
解釋:
可以說是通過遞迴的方式,通過一個協程執行完成再來產生第二個協程的方式, 本人是通過輪訓channels的slice來完成協程之間的同步任務, 如果還有更好的方式請留言哦。 就算你有100W個也不懼怕, 看你配置了。
結語
就寫到這裡吧,詳細的最佳化後期再完成, 細嚼才能慢咽啊!
建議很重要,交流進步才是硬道理啊!