Channel是Go中的一個核心類型,可以把它看成一個管道,Goroutine通過它可以發(fā)送或者接收數(shù)據(jù)并進行通訊屑柔。它的操作符號是 <- ,就像map和slice一樣,channel必須先創(chuàng)建才能夠使用:
ch=make(chan int) // int表示chan里面的數(shù)據(jù)類型
ch :=make(chan interface{}) //任意數(shù)據(jù)類型的chan
Channel類型
Channel類型的定義格式如下:
ChannelType = ( "chan" | "chan" "<-" | "<-" "chan" ) ElementType .
包括三種類型的定義漫仆。 <- 表示數(shù)據(jù)的流向,如果沒有指定 <-坤按,則表示chan既可以發(fā)送數(shù)據(jù)也可以接受數(shù)據(jù)毯欣。
Channel聲明
chan int //既可以發(fā)送數(shù)據(jù)也可以接受數(shù)據(jù)
<- chan int //只允許從chan接受int類型數(shù)據(jù)
chan <- int //只允許發(fā)送int類型數(shù)據(jù)到chan
Channel設(shè)置容量
make(chan int ,100)
// Channel: The channel's buffer is initialized with the specified
// buffer capacity. If zero, or the size is omitted, the channel is
// unbuffered.
func make(t Type, size ...IntegerType) Type
參數(shù)100用來設(shè)置channel的緩存容量,也是channel最大可以容納的元素數(shù)量臭脓。
元素數(shù)量超過緩存大小酗钞,向channel發(fā)送數(shù)據(jù)會阻塞。
元素數(shù)量為空来累,從channel接受數(shù)據(jù)會阻塞砚作。
如果緩存大小設(shè)置為0,發(fā)送和接受都將被阻塞嘹锁。
Channel類型安全
多個goroutine向channel發(fā)送或者接受數(shù)據(jù)葫录,是線程安全的,不要采取額外的同步措施领猾。
Channel數(shù)據(jù)順序
chnnel是一個FIFO(先進先出)隊列米同,向channel發(fā)送數(shù)據(jù)的順序和從channel接受數(shù)據(jù)的順序一致。
Channel closed
channel關(guān)閉之后摔竿,繼續(xù)向channel發(fā)送數(shù)據(jù)會panic,從channel接受數(shù)據(jù)不會panic面粮,會立即返回,返回的是chan數(shù)據(jù)類型的零值继低。
ch :=make(chan int,10)
close(ch)
a :=<- ch
println(a) //print 0
//ch <- 1 //panic: send on closed channel
我們也可以通過以下代碼來檢查channel是否關(guān)閉
ch :=make(chan int,10)
close(ch)
a,ok :=<-ch //ok==false熬苍,表示channel被關(guān)閉
利用這個特性我們可以用來為中間的任務(wù)執(zhí)行步驟的設(shè)置超時時間,超過指定時間任務(wù)沒有執(zhí)行完成袁翁,關(guān)閉channel柴底,主邏輯就繼續(xù)向下執(zhí)行。
Channel 遍歷
通過range可以遍歷輸出channel中的元素粱胜,channle 被close之后依然會輸出channel中的元素似枕。以下代碼是向緩沖區(qū)為10的元素發(fā)送數(shù)據(jù),然后關(guān)閉channle之后依然可以遍歷輸出channel里面的元素:
func main() {
c := make(chan int,10)
for i := 0; i < 10; i = i + 1 {
c <- i
}
close(c)
for i := range c {
fmt.Print(i)
}
}
//輸出:0123456789
下面代碼是向一個沒有緩沖區(qū)的channel發(fā)送數(shù)據(jù)年柠,通過range遍歷從channel接受數(shù)據(jù)打印:
func main() {
c := make(chan int)
go func() {
for i := 0; i < 10; i = i + 1 {
c <- i
}
close(c) //必須close,否則遍歷channel后會panic褪迟。會提示:fatal error: all goroutines are asleep - deadlock!
}()
for i := range c {
fmt.Println(i)
}
fmt.Println("Finished")
}
Channel用于select語句
select語句選擇一組send或者receive操作去處理冗恨。它的case可以是send語句,也可以是receive語句味赃,亦或default掀抹。如果case有多個channel可以接受數(shù)據(jù),那么Go會隨機選擇一個case去處理心俗,如果沒有任何一個case要處理傲武,有default的情況下會執(zhí)行default邏輯蓉驹,如果沒有default項,則會一直阻塞揪利,直到滿足某個case條件态兴。
下面代碼是打印斐波那契數(shù)列每一項,通過在select語句之外嵌套for循環(huán)可以一直處理,直到遇到退出語句疟位。
package main
import "fmt"
func fibonacci(c, quit chan int) {
x, y := 0, 1
for {
select {
case c <- x:
x, y = y, x+y
case <-quit:
fmt.Println("quit")
return
}
}
}
func main() {
c := make(chan int)
quit := make(chan int)
go func() {
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
quit <- 0
}()
fibonacci(c, quit)
}
打印結(jié)果
0
1
1
2
3
5
8
13
21
34
quit
Channel用于timeout
select case使用channel會一直阻塞知道滿足某個case條件才會退出瞻润。不過我們可以通過time.After(timeout) 設(shè)置超時時間,超時時間到后就會自動退出甜刻。
下面代碼中channel c1休眠2秒后會寫入數(shù)據(jù)绍撞,然后case滿足,打印result 1得院。
不過打印的是timeout 1傻铣,因為第二個case語句設(shè)置了超時時間。
package main
import "time"
import "fmt"
func main() {
c1 := make(chan string, 1)
go func() {
time.Sleep(time.Second * 2)
c1 <- "result 1"
}()
select {
case res := <-c1:
fmt.Println(res)
case <-time.After(time.Second * 1):
fmt.Println("timeout 1")
}
}
打印結(jié)果
timeout 1
Channle groutine同步
利用channel我們可以實現(xiàn)groutine之間相互通信祥绞。
下面的例子是啟動一個groutine區(qū)完成任務(wù)非洲,任務(wù)完成后想channel寫入數(shù)據(jù)。
main方法收到寫入數(shù)據(jù)信號就打印done,表示任務(wù)完成就谜,否則一直阻塞知道groutine完成任務(wù)怪蔑。
package main
import (
"time"
)
func worker(done chan bool) {
time.Sleep(time.Second)
// 通知任務(wù)已完成
done <- true
}
func main() {
done := make(chan bool, 1)
go worker(done)
// 等待任務(wù)完成
<-done
println("done")
}