## Channel
那么如果啟動了多個 goroutine,它們之間該如何通信呢?這就是 Go 語言提供的 channel(通道)要解決的問題。
**創建一個 channel**
```
ch := make(chan 數據類型)
ch1 := make(chan int) // 創建一個整型類型的通道
ch2 := make(chan interface{}) // 創建一個空接口類型的通道, 可以存放任意格式
type Equip struct{ /* 一些字段 */ }
ch2 := make(chan *Equip) // 創建Equip指針類型的通道, 可以存放*Equip
```
**發送數據**
通道的發送使用特殊的操作符 `<-`,將數據通過通道發送的格式為:
通道變量 <- 值
使用 `make` 創建一個通道后,就可以使用`<-`向通道發送數據,代碼如下:
```
// 創建一個空接口通道
ch := make(chan interface{})
// 將0放入通道中
ch <- 0
// 將hello字符串放入通道中
ch <- "hello"
```
把數據往通道中發送時,如果接收方一直都沒有接收,那么發送操作將持續阻塞。
**接收數據**
通道接收同樣使用`<-`操作符
通道的數據接收一共有以下寫法。
1. 阻塞接收數據
阻塞模式接收數據時,將接收變量作為`<-`操作符的左值,格式如下:
```
data := <-ch
```
執行該語句時將會阻塞,直到接收到數據并賦值給 data 變量。
2. 忽略接收的數據
阻塞接收數據后,忽略從通道返回的數據,格式如下:
```
<-ch
```
執行該語句時將會發生阻塞,直到接收到數據,但接收到的數據會被忽略。這個方式實際上只是通過通道在 goroutine 間阻塞收發實現并發同步。
3. 循環接收
通道的數據接收可以借用 for range 語句進行多個元素的接收操作,格式如下:
```
for data := range ch {
}
```
通道 ch 是可以進行遍歷的,遍歷的結果就是接收到的數據。數據類型就是通道的數據類型。通過 for 遍歷獲得的變量只有一個,即上面例子中的 data。
```
package main
import (
"fmt"
"time"
)
func main() {
// 構建一個通道
ch := make(chan int)
// 開啟一個并發匿名函數
go func() {
// 從3循環到0
for i := 3; i >= 0; i-- {
// 發送3到0之間的數值
ch <- i
// 每次發送完時等待
time.Sleep(time.Second)
}
}()
// 遍歷接收通道數據
for data := range ch {
// 打印通道數據
fmt.Println(data)
// 當遇到數據0時, 退出接收循環
if data == 0 {
break
}
}
}
```
### 無緩沖 channel
上面的示例中,使用 make 創建的 chan 就是一個無緩沖 channel,它的容量是 0,不能存儲任何數據。所以無緩沖 channel 只起到傳輸數據的作用,數據并不會在 channel 中做任何停留。這也意味著,無緩沖 channel 的發送和接收操作是同時進行的,它也可以稱為同步 channel。
### 有緩沖 channel
有緩沖 channel 類似一個可阻塞的隊列,內部的元素先進先出。通過 make 函數的第二個參數可以指定 channel 容量的大小,進而創建一個有緩沖 channel,如下面的代碼所示:
```
cacheCh:=make(chan int,5)
```
一個有緩沖 channel 具備以下特點:
1. 有緩沖 channel 的內部有一個緩沖隊列;
2. 發送操作是向隊列的尾部插入元素,如果隊列已滿,則阻塞等待,直到另一個 goroutine 執行,接收操作釋放隊列的空間;
3. 接收操作是從隊列的頭部獲取元素并把它從隊列中刪除,如果隊列為空,則阻塞等待,直到另一個 goroutine 執行,發送操作插入新的元素。
因為有緩沖 channel 類似一個隊列,可以獲取它的容量和里面元素的個數。如下面的代碼所示:
```
cacheCh:=make(chan int,5)
cacheCh <- 2
cacheCh <- 3
fmt.Println("cacheCh容量為:",cap(cacheCh),",元素個數為:",len(cacheCh))
```
通過內置函數 cap 可以獲取 channel 的容量,也就是最大能存放多少個元素,通過內置函數 len 可以獲取 channel 中元素的個數。
### 單向 channel
有時候,我們有一些特殊的業務需求,比如限制一個 channel 只可以接收但是不能發送,或者限制一個 channel 只能發送但不能接收,這種 channel 稱為單向 channel。
單向 channel 的聲明也很簡單,只需要在聲明的時候帶上 <- 操作符即可,如下面的代碼所示:
```
// 接收通道
ch := make(chan<- int)
// 發送通道
ch := make(<-chan int)
```
### 關閉 channel
channel 還可以使用內置函數 `close`關閉。關閉channel之后就無法再發送任何數據了,在消費方可以通過語法`v, ok := <-ch`獲取channel是否被關閉。如果ok返回false,那么說明channel已經沒有任何數據并且已經被關閉。