題目:
現在有兩個goroutine。
一個輸出1、3、5、7、9……
另一個輸出2、4、6、8、10……
寫一段代碼,讓他們輸出1、2、3、4、5、6、7、8、9、10……
解法:
使用Go的channel來解決比較合適。
需要3個channel。
A通道用來記錄A協程的狀態。
B通道用來記錄B協程的狀態。
Exit通道用來阻塞主協程,使程序不要立即退出,而是等待我們發出退出信號以后才退出。
Go的channel具有阻塞特性,無緩沖通道中只能存放一個數據。
通道最初是空的,如果想從空通道中讀取一個數據,程序就會阻塞,直到向通道中寫入一個數據。
如果通道中有一個數據,卻沒有人來讀,程序也將阻塞,直到有人將這個數據讀走。
利用這個特性,我們在主goroutine中讀取Exit通道,因為我們還沒有向Exit通道中寫數據,主goroutine將在此阻塞。
主goroutine雖然阻塞了,其他goroutine卻是可以正常運行的。
我們設計,其他goroutine把自己的工作都做完之后,向Exit通道寫入一個數據。
主goroutine得到這個數據之后,程序不再阻塞,就可以正常退出了。
這樣我們就實現了最基本的,有多個goroutine時,工作沒做完時等待,都做完后退出的功能了。
然后我們來設計AB兩個通道。
利用無緩沖通道阻塞的特性,我們設計:
A協程輸出一個數后,向B通道寫入一個數據。
B協程輸出一個數后,也向A通道寫入一個數據。
AB都會時刻檢測自己的通道中有沒有數據,但是就像接力棒一樣,只有另一個協程可以向當前協程的通道傳遞數據。
而且傳遞完之后必須等待。
這就保證了,A干活的時候,B必須等着,不能跟A搶活干。
A干完之后,權利交給B,B干完之后,權利再交給A,直到倆人都把活干完。
這就是AB協程交替輸出的中間過程,我們還需要設計開始和結束。
開始時,我們在主goroutine中,手動向A協程發送一個開始的信號。
結束時,最后一個把活干完的人(B),向Exit通道中發送一個數據,使主goroutine退出。
實現代碼:
package main
import (
"fmt"
)
func main() {
// 創建3個channel,A,B和Exit
A := make(chan bool)
B := make(chan bool)
Exit := make(chan bool)
go func() {
// 如果A通道是true,我就執行
for i := 1; i <= 10; i += 2 {
if ok := <-A; ok {
fmt.Println("A 輸出", i)
B <- true
}
}
}()
go func() {
defer func() { Exit <- true }() // 這個協程的活干完之后,向主goroutine發送信號
// 如果B通道是true,我就執行
for i := 2; i <= 10; i += 2 {
if ok := <-B; ok {
fmt.Println("B 輸出", i)
if i != 10 { // r如果i等於10了,就不要再向A通道寫數據了,否則將導致A通道死鎖,至於為什么,坦白說我很疑惑
A <- true
}
}
}
}()
A <- true // 啟動條件
<-Exit // 結束條件
}