1. 程式人生 > >Golang--Directional Channel(定向通道)

Golang--Directional Channel(定向通道)

# 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