Golang--Directional Channel(定向通道)
阿新 • • 發佈:2021-01-31
# Directional Channel
> 通道可以是定向的(`directional`)。在預設情況下,通道將以雙向的(`bidirectional`)形式運作,使用者既可以把值放人通道,也可以從通道取出值;但是,通道也可以被限制為只能執行傳送操作(`send-only`)或者只能執行接收操作(`receive-only`)。
通常可以叫**定向通道**,也有人叫**單向通道**,兩者其實都是指向這篇短文要討論的`Directional Channel`。
下面直接舉例子說明:
~~~go
package onlyChannelTest
import (
"fmt"
"math/rand"
"sync"
"testing"
"time"
)
func TestOnlyChannel(t *testing.T) {
var wg sync.WaitGroup
wg.Add(2)
c:= make(chan int, 3)
var send chan<- int = c
var recv <-chan int = c
go func() {
defer wg.Done()
for i := 0; i < 6; i++ { // 注意這裡是 6
fmt.Print("receive\n")
fmt.Println(<-recv)
}
}()
go func() {
defer wg.Done()
defer close(c)
rand.Seed(time.Now().UTC().UnixNano())
for i := 0; i < 3; i++ { // 這裡是 3
fmt.Print("send\n")
send <- rand.Intn(100)
}
}()
wg.Wait()
}
~~~
這裡可以先猜一下執行結果,我直接貼出:
![image-20210131113121061](https://i.loli.net/2021/01/31/xycwHCORvXohreG.png)
後面的引起了我的注意:
~~~shell
receive
0
~~~
是最讓我困惑的地方,因為在一般印象內`channel`都是會阻塞的存在,而從上面結果看,第一個`recv`也是阻塞了)。但是後面的的`recv`卻沒有起到阻塞的作用,還是把`c`中的預設值`0`輸出了。
通過`debug`,不難看出的是,兩個單向通道的傳送和接收都是從一個緩衝通道中進行操作的,兩個單項通道同屬於一個雙向通道,只不過他們被分別限制了接收和傳送功能。
![image-20210131115618137](https://i.loli.net/2021/01/31/8u4yqJEYFkwzDBl.png)
通過`debug`,再次發現,在`recv`讀取完之後,通道內並沒有被**“清空”**,而是保有預設值。
![image-20210131122218881](https://i.loli.net/2021/01/31/XYczhmC1qUbfTME.png)
難道,`recv`並沒有被阻塞?
驗證方法如下:
~~~go
// defer close(c)
~~~
選擇註釋掉髮送`Goroutine`中的關閉通道。
![image-20210131123122579](https://i.loli.net/2021/01/31/dpuj61G9Z8EWKls.png)
結果很明顯,造成死鎖,證明==兩個單向通道之間是有通訊的,有阻塞的==。
最後通過原始碼,還是找到了答案:
![image-20210131123806000](https://i.loli.net/2021/01/31/87xhoPdEIclRa4f.png)
由此,為避免接收到不需要的無效**零值**,應修改接收`Goroutine`--`receive`的寫法:
~~~go
go func() {
defer wg.Done()
for i := 0; i < 6; i++ { // 注意這裡是 6
x, ok := <-recv
if ok {
fmt.Print("receive\n")
fmt.Println(x)
}else{
break
}
}
}()
~~~
![image-20210131124040698](https://i.loli.net/2021/01/31/1C9J6zrgesIx