Go - Channel 原理

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

註:該文原文為 Channel Axioms ,作者是 Dave Cheney,這是他的部落格地址

大部分的新的 Go 程式員能快速理解 channel 是作為一個 queue 的值和認同當 channel 是滿的或者是空的時候, 操作是阻塞的概念。

這篇文章探討了 channel 四個不太常見的特性:

  • 給一個 nil channel 發送資料,造成永遠阻塞
  • 從一個 nil channel 接收資料,造成永遠阻塞
  • 給一個已經關閉的 channel 發送資料,引起 panic
  • 從一個已經關閉的 channel 接收資料,立即返回一個零值

給一個 nil channel 發送資料,造成永遠阻塞

這第一個例子對於新來者是有點小驚奇的,它給一個 nil channel 發送資料,造成永遠阻塞。

以下這個程式將在第5行造成死結,因為未初始化的 channel 是 nil 的,其值是零

package mainfunc main() {        var c chan string        c <- "let's get started" // deadlock}

點擊這裡運行

從一個 nil channel 接收資料,造成永遠阻塞

類似的,從一個 nil channel 接收資料,會造成接受者永遠阻塞。

package mainimport "fmt"func main() {        var c chan string        fmt.Println(<-c) // deadlock}

點擊這裡運行

為什麼會發生這樣的情況?下面是一個可能的解釋

  • channel 的 buffer 的大小不是型別宣告的一部分,因此它必須是 channel 的值的一部分
  • 如果 channel 未被初始化,它的 buffer 的大小將是0
  • 如果 channel 的 buffer 大小是0,那麼它將沒有 buffer
  • 如果 channel 沒有 buffer,一個發送將會被阻塞,直到另外一個 goroutine 為接收做好了準備
  • 如果 channel 是 nil 的,並且接收者和寄件者沒有任何互動,他們都會阻塞然後在各自的 channel 中等待以及不再被解除阻塞狀態

給一個已經關閉的 channel 發送資料,引起 panic

以下程式將有可能 panic,因為在它的兄弟姐妹有時間完成發送他們的值之前,這第一個 goroutine 在達到10的時候將關閉 channel。

package mainimport "fmt"func main() {        var c = make(chan int, 100)        for i := 0; i < 10; i++ {                go func() {                        for j := 0; j < 10; j++ {                                c <- j                        }                        close(c)                }()        }        for i := range c {                fmt.Println(i)        }}

點擊這裡運行

因此為什麼沒有一個 close() 版本能讓你檢測 channel 是否關閉?

if !isClosed(c) {        // c isn't closed, send the value        c <- v}

但是這個函數有一個內在的競爭,某個人可能在我們檢查完 isClosed(c) 之後,但是代碼擷取 c <- v 之前關閉這個 channel。

處理這個問題的方法在被串連在該文章底部的 2nd article 被討論。

從一個已經關閉的 channel 接收資料,立即返回一個零值

這最後一個樣本與前一個是相反的,一旦一個 channel 被關閉,它的所有的值都會從 buffer 中流失,channel 將立即返回0值。

package mainimport "fmt"func main() {            c := make(chan int, 3)            c <- 1            c <- 2            c <- 3            close(c)            for i := 0; i < 4; i++ {                        fmt.Printf("%d ", <-c) // prints 1 2 3 0            }}

點擊這裡運行

針對這個問題的正確的解決辦法是使用 range 迴圈處理:

for v := range c {            // do something with v}for v, ok := <- c; ok ; v, ok = <- c {            // do something with v}

這兩個語句在函數中是相等的,展示 range 是做什麼。

擴充閱讀

  • Concurrency is not Parallelism by Rob Pike
  • Go Concurrency Patterns: Pipelines and cancellation
  • Curious Channels

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.