golang中channel的用法
channel
有點類似於管道,它在goroutine
同步與通訊中,有著起承轉合的作用,同時也是Golang
實現ofollow,noindex">CSP
模型的關鍵
package main func main() { senderOnly := make(chan<- int)// 只能用來發送(管道的入口,只進不出) receiverOnly := make(<-chan int) // 只能用來接收(管道的出口,只出不進) unbuffer := make(chan int)// 無緩衝可收發 buffer := make(chan int, 2)// 無緩衝可收發 println(senderOnly, receiverOnly, unbuffer, buffer) }
以下是channel
的一些使用場景
等待goroutine完成
package main func main() { println("start main") ch := make(chan bool) go func() { println("come into goroutine") ch <- true }() println("do something else") <-ch close(ch) println("end main") }
列印結果:
start main do something else come into goroutine end main
多個goroutine協同
三個功能不相關的goroutine
最後結果要彙總累加到result
上
package main func main() { println("start main") ch := make(chan int) var result int go func() { println("come into goroutine1") var r int for i := 1; i <= 10; i++ { r += i } ch <- r }() go func() { println("come into goroutine2") var r int = 1 for i := 1; i <= 10; i++ { r *= i } ch <- r }() go func() { println("come into goroutine3") ch <- 11 }() for i := 0; i < 3; i++ { result += <-ch } close(ch) println("result is:", result) println("end main") }
其中一組列印結果:
start main come into goroutine3 come into goroutine2 come into goroutine1 result is: 3628866 end main
Select
兩個goroutine
無直接關聯,但其中一個先達到某一設定條件便退出或超時退出
package main import "time" func main() { println("start main") cond1 := make(chan int) cond2 := make(chan uint64) go func() { for i := 0; ; i++ { cond1 <- i } }() go func() { var i uint64 for ; ; i++ { cond2 <- i } }() endCond := false for endCond != true { select { case a := <-cond1: if a > 99 { println("end with cond1") endCond = true } case b := <-cond2: if b == 100 { println("end with cond2") endCond = true } case <-time.After(time.Microsecond): println("end with timeout") endCond = true } } println("end main") }
其中列印結果有可能是:
start main end with cond1 end main
也有可能是:
start main end with timeout end main
也可能是:
start main end with cond2 end main
這說明迴圈100次大概需要1微秒的時間
channel與range
package main import "fmt" func main() { println("start main") ch := make(chan int, 4) go func() { for i := 0; i < 10; i++ { ch <- i } // 如果不關閉channel,會引發panic close(ch) }() for v := range ch { fmt.Println(v) } println("end main") }
列印結果為:
start main 0 1 2 3 4 5 6 7 8 9 end main
無緩衝channel
package main func main() { var ch = make(chan int) ch <- 1 println(<-ch) }
列印結果為:
fatal error: all goroutines are asleep - deadlock! goroutine 1 [chan send]: main.main() /tmp/sandbox117018544/main.go:5 +0x60
死鎖了,為什麼會這樣呢,因為ch
是一個無緩衝的channel
,在執行到ch <- 1
就阻塞了當前goroutine
(也就是main函式所在的goroutine
),後面列印語句根本沒機會執行
稍加修改即能正常執行
在playground中執行package main func main() { var ch = make(chan int) go func() { ch <- 1 println("sender") }() println(<-ch) }
因為此時ch
既有傳送也有接收而且不在同一個goroutine
裡面,此時它們不會相互阻塞