深入理解 Golang Channel

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

0. 引言

channel 是 Go 語言中的一個非常重要的特性,這篇文章來深入瞭解一下 channel。

1. CSP

要想理解 channel 要Crowdsourced Security Testing道 CSP 模型。CSP 是 Communicating Sequential Process 的簡稱,中文可以叫做通訊順序進程,是一種並發編程模型,由 Tony Hoare 於 1977 年提出。簡單來說,CSP 模型由並發執行的實體(線程或者進程)所組成,實體之間通過發送訊息進行通訊,這裡發送訊息時使用的就是通道,或者叫 channel。CSP 模型的關鍵是關注 channel,而不關注發送訊息的實體。Go 語言實現了 CSP 部分理論,goroutine 對應 CSP 中並發執行的實體,channel 也就對應著 CSP 中的 channel。

2. channel 基礎知識

2.1 建立 channel

channel 使用之前需要通過 make 建立。

1
2
unBufferChan := make(chan int)  // 1
bufferChan := make(chan int, N) // 2

上面的方式 1 建立的是無緩衝 channel,方式 2 建立的是緩衝 channel。如果使用 channel 之前沒有 make,會出現 dead lock 錯誤。至於為什麼是 dead lock,下文我們從源碼裡面看看。

1
2
3
4
5
6
7
func main() {
var x chan int
go func() {
x <- 1
}()
<-x
}
1
2
3
4
5
6
7
8
9
$ go run channel1.go
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive (nil chan)]:
main.main()
/Users/kltao/code/go/examples/channl/channel1.go:11 +0x60

goroutine 4 [chan send (nil chan)]:
main.main.func1(0x0)
2.2 channel 讀寫操作
1
2
3
4
5
6
7
ch := make(chan int, 10)

// 讀操作
x <- ch

// 寫操作
ch <- x
2.3 channel 種類

channel 分為無緩衝 channel 和有緩衝 channel。兩者的區別如下:

  • 無緩衝:發送和接收動作是同時發生的。如果沒有 goroutine 讀取 channel (<- channel),則寄件者 (channel <-) 會一直阻塞。

  • 緩衝:緩衝 channel 類似一個有容量的隊列。當隊列滿的時候寄件者會阻塞;當隊列空的時候接收者會阻塞。

2.4 關閉 channel

channel 可以通過 built-in 函數 close() 來關閉。

1
2
3
4
ch := make(chan int)

// 關閉
close(ch)

關於關閉 channel 有幾點需要注意的是:

  • 重複關閉 channel 會導致 panic。
  • 向關閉的 channel 發送資料會 panic。
  • 從關閉的 channel 讀資料不會 panic,但是讀出的資料是 channel 類似的預設值,比如 chan int 類型的 channel 關閉之後讀取到的值為 0。

對於上面的第三點,我們需要區分一下:channel 中的值是預設值還是 channel 關閉了。可以使用 ok-idiom 方式,這種方式在 map 中比較常用。

1
2
3
4
5
6
7
8
9
ch := make(chan int, 10)
...
close(ch)

// ok-idiom
val, ok := <-ch
if ok == false {
// channel closed
}

3. channel 的典型用法

1. goroutine 通訊
1
2
3
4
5
6
7
func main() {
x := make(chan int)
go func() {
x <- 1
}()
<-x
}
2. select

select 一定程度上可以類比於 linux 中的 IO 多工中的 select。後者相當於提供了對多個 IO 事件的統一管理,而 Golang 中的 select 相當於提供了對多個 channel 的統一管理。當然這隻是 select 在 channel 上的一種使用方法。

1
2
3
4
5
6
7
select {
case e, ok := <-ch1:
...
case e, ok := <-ch2:
...
default:
}

值得注意的是 select 中的 break 只能跳到 select 這一層。select 使用的時候一般配合 for 迴圈使用,像下面這樣,因為正常 select 裡面的流程也就執行一遍。這麼來看 select 中的 break 就稍顯雞肋了。所以使用 break 的時候一般配置 label 使用,label 定義在 for 迴圈這一層。

1
2
3
4
5
for {
select {
...
}
}
3. range channel

range channel 可以直接取到 channel 中的值。當我們使用 range 來操作 channel 的時候,一旦 channel 關閉,迴圈自動結束。

1
2
3
4
5
6
7
8
9
10
11
12
func consumer(ch chan int) {
for x := range ch {
fmt.Println(x)
...
}
}

func producer(ch chan int) {
for _, v := range values {
ch <- v
}
}
4. 逾時控制

在很多操作情況下都需要逾時控制,利用 select 實現逾時控制,下面是一個簡單的樣本。

1
2
3
4
5
6
select {
case <- ch:
// get data from ch
case <- time.After(2 * time.Second)
// read data from ch timeout
}

類似的,上面的 time.After 可以換成其他的任何異常控制流程。

5. 生產者-消費者模型

利用緩衝 channel 可以很輕鬆的實現生產者-消費者模型。上面的 range 樣本其實就是一個簡單的生產者-消費者模型實現。

4. 單向 channel

單向 channel,顧名思義只能寫或讀的 channel。但是仔細一想,只能寫的 channel,如果不讀其中的值有什麼用呢?其實單向 channel 主要用在函式宣告中。比如。

1
func foo(ch chan<- int) <-chan int {...}

foo 的形參是一個只能寫的 channel,那麼就表示函數 foo 只會對 ch 進行寫,當然你傳入的參數可以是個普通 channel。foo 的傳回值是一個只能讀的 channel,那麼表示 foo 的傳回值規範用法就是只能讀取。這種寫法在 Golang 的原生程式碼程式庫中有非常多的樣本,感興趣的可以去看一下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Done returns a channel which is closed if and when this pipe is closed
// with CloseWithError.
func (p *http2pipe) Done() <-chan struct{} {
p.mu.Lock()
defer p.mu.Unlock()
if p.donec == nil {
p.donec = make(chan struct{})
if p.err != nil || p.breakErr != nil {

p.closeDoneLocked()
}
}
return p.donec
}

也許你會說這麼寫在功能上和使用普通的 channel 並不會有什麼差別。確實是這樣的。但是使用單向 channel 編程體現了一種非常優秀的編程範式:convention over configuration,中文一般叫做 約定優於配置。這種編程範式在 Ruby 中體現的尤為明顯。

5. 總結

Golang 的 channel 將 goroutine 隔離開,並發編程的時候可以將注意力放在 channel 上。在一定程度上,這個和訊息佇列的解耦功能還是挺像的。上面主要還是介紹了一些 channel 的常規操作,還有一些奇淫技巧放在參考資料裡了。之後的一篇文章還是來看看 channel 的源碼吧,對於更深入地理解 channel 還是挺有用的。

6. 參考

  1. Go Concurrency Patterns: Pipelines and cancellation
  2. Go Concurrency Visualize
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.