go中channel是一種重要的引用資料型別,可以看作是管道,可以通過併發的核心單元傳送或者接收資料進行通訊。
操作符 <-
,箭頭指向,即資料的流向,沒有指明方向,那麼Channel就是雙向的,既可以接收資料,也可以傳送資料
ch <- v // 傳送值到Channel ch中
v := <-ch // 從Channel ch中接收資料,並賦值給v
// 初始化 Channel ch, 類似於Slice,map的資料型別一樣,channel必須先建立,然後再使用
ch := make(chan int)
複製程式碼
Channel的方向
- 不確定雙向性、指定唯一單向
chan T // 既可以接收資料,也可以傳送資料
chan<- float64 // 只可以用來傳送 float64 型別的資料
<-chan int // 只可以接收 float64 資料型別的資料
複製程式碼
<-總是優先和最左邊的型別結合
chan<- chan int // 等價 chan<- (chan int)
chan<- <-chan int // 等價 chan<- (<-chan int)
<-chan <-chan int // 等價 <-chan (<-chan int)
chan (<-chan int)
複製程式碼
設定Channel的容量,代表Channel快取的大小
make初始化的時候可以設定 如果沒有設定Channel的大小,或者設定為0,那麼預設Channel的沒有快取的,當接收者和傳送者都準備好了後,他們通訊才會發生阻塞(blocking) 如果我們設定了快取的大小,那麼就有可能不會傳送阻塞了,只有buffer滿了後,傳送者才會阻塞,只有清空了快取,接收者才會阻塞。 一個nil的Channel不會通訊。
make(chan int, 100)
複製程式碼
關閉Channel
內建的方法close()
可以關閉Channel,如何檢測Channel是否被關閉
defer close(c) // 關閉 channel
v, ok := <-ch // 檢測 channel是否被關閉
複製程式碼
select的使用,類似於switch
case
用可能處理的接收語句,有可能是傳送語句,還有可能是預設 default
timeout 超時處理
如果沒有case需要處理,select語句就會一直阻塞著
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")
}
}
複製程式碼
其實使用time.After
函式,它是返回一個<-chan Time
的單向channel,在指定時間傳送一個當前時間給返回的channel中
同步
channel可以用在goroutine之間的同步
import (
"fmt"
"time"
)
func worker(done chan bool) {
time.Sleep(time.Second)
// 通知任務已完成
done <- true
}
func main() {
done := make(chan bool, 1)
go worker(done)
// 等待任務完成
<-done
}
複製程式碼
生產者和消費者
package main
import (
"fmt"
"time"
)
func main() {
fmt.Println("show channel")
queue := make(chan int, 1)
go test.Producer(queue)
go test.Consumer(queue)
time.Sleep(1e9)
}
func Producer(queue chan<- int) {
for i := 0; i < 10; i++ {
fmt.Println("send:", i)
queue<- i+10000
}
}
func Consumer(queue <-chan int) {
for i := 0; i < 10; i++ {
v := <-queue
fmt.Println("receive:", v)
}
}
複製程式碼