《Go語言四十二章經(jīng)》第二十二章 通道(channel)

《Go語言四十二章經(jīng)》第二十二章 通道(channel)

作者:李驍

22.1 通道(channel)

Go 奉行通過通信來共享內(nèi)存,而不是共享內(nèi)存來通信。所以,channel 是goroutine之間互相通信的通道,goroutine之間可以通過它發(fā)消息和接收消息。

channel是進(jìn)程內(nèi)的通信方式,因此通過channel傳遞對象的過程和調(diào)用函數(shù)時(shí)的參數(shù)傳遞行為比較一致,比如也可以傳遞指針等。

channel是類型相關(guān)的,一個(gè)channel只能傳遞(發(fā)送或接受 | send or receive)一種類型的值,這個(gè)類型需要在聲明channel時(shí)指定。

默認(rèn)的,信道的存消息和取消息都是阻塞的 (叫做無緩沖的信道)

使用make來建立一個(gè)通道:

var channel chan int = make(chan int)
// 或
channel := make(chan int)

Go中channel可以是發(fā)送(send)、接收(receive)、同時(shí)發(fā)送(send)和接收(receive)。

// 定義接收的channel
receive_only := make (<-chan int)
 
// 定義發(fā)送的channel
send_only := make (chan<- int)

// 可同時(shí)發(fā)送接收
send_receive := make (chan int)
  • chan<- 表示數(shù)據(jù)進(jìn)入通道,要把數(shù)據(jù)寫進(jìn)通道,對于調(diào)用者就是發(fā)送。
  • <-chan 表示數(shù)據(jù)從通道出來,對于調(diào)用者就是得到通道的數(shù)據(jù),當(dāng)然就是接收。

定義只發(fā)送或只接收的channel意義不大,一般用于在參數(shù)傳遞中:

package main

import (
    "fmt"
    "time"
)

func main() {
    c := make(chan int) // 不使用帶緩沖區(qū)的channel
    go send(c)
    go recv(c)
    time.Sleep(3 * time.Second)
close(c)
}

// 只能向chan里send數(shù)據(jù)
func send(c chan<- int) {
    for i := 0; i < 10; i++ {

        fmt.Println("send readey ", i)
        c <- i
        fmt.Println("send ", i)
    }
}

// 只能接收channel中的數(shù)據(jù)
func recv(c <-chan int) {
    for i := range c {
        fmt.Println("received ", i)
    }
}
程序輸出:

send readey  0
send  0
send readey  1
received  0
received  1
send  1
send readey  2
send  2
send readey  3
received  2
received  3
send  3
send readey  4
send  4
send readey  5
received  4
received  5
send  5
send readey  6
send  6
send readey  7
received  6
received  7
send  7
send readey  8
send  8
send readey  9
received  8
received  9
send  9

運(yùn)行結(jié)果上我們可以發(fā)現(xiàn)一個(gè)現(xiàn)象,往channel 發(fā)送數(shù)據(jù)后,這個(gè)數(shù)據(jù)如果沒有取走,channel是阻塞的,也就是不能繼續(xù)向channel 里面發(fā)送數(shù)據(jù)。因?yàn)樯厦娲a中,我們沒有指定channel 緩沖區(qū)的大小,默認(rèn)是阻塞的。

我們可以建立帶緩沖區(qū)的 channel:

c := make(chan int, 1024)

我們把前面的程序修改下:

package main

import (
    "fmt"
    "time"
)

func main() {
    c := make(chan int, 10) // 使用帶緩沖區(qū)的channel
    go send(c)
    go recv(c)
    time.Sleep(3 * time.Second)
    close(c)
}

// 只能向chan里send發(fā)送數(shù)據(jù)
func send(c chan<- int) {
    for i := 0; i < 10; i++ {

        fmt.Println("send readey ", i)
        c <- i
        fmt.Println("send ", i)
    }
}

// 只能接收channel中的數(shù)據(jù)
func recv(c <-chan int) {
    for i := range c {
        fmt.Println("received ", i)
    }
}
程序輸出:

send readey  0
send  0
send readey  1
send  1
send readey  2
send  2
send readey  3
send  3
send readey  4
send  4
send readey  5
received  0
received  1
received  2
received  3
received  4
received  5
send  5
send readey  6
send  6
send readey  7
send  7
send readey  8
send  8
send readey  9
send  9
received  6
received  7
received  8
received  9

從運(yùn)行結(jié)果我們可以看到(每次執(zhí)行順序不一定相同,goroutine 運(yùn)行導(dǎo)致的原因),帶有緩沖區(qū)的channel,在緩沖區(qū)有數(shù)據(jù)而未填滿前,讀取不會出現(xiàn)阻塞的情況。

  • 無緩沖的通道(unbuffered channel)是指在接收前沒有能力保存任何值的通道。

這種類型的通道要求發(fā)送 goroutine 和接收 goroutine 同時(shí)準(zhǔn)備好,才能完成發(fā)送和接收操作。如果兩個(gè)goroutine沒有同時(shí)準(zhǔn)備好,通道會導(dǎo)致先執(zhí)行發(fā)送或接收操作的 goroutine 阻塞等待。

這種對通道進(jìn)行發(fā)送和接收的交互行為本身就是同步的。

  • 有緩沖的通道(buffered channel)是一種在被接收前能存儲一個(gè)或者多個(gè)值的通道。

這種類型的通道并不強(qiáng)制要求 goroutine 之間必須同時(shí)完成發(fā)送和接收。通道會阻塞發(fā)送和接收動(dòng)作的條件也會不同。只有在通道中沒有要接收的值時(shí),接收動(dòng)作才會阻塞。只有在通道沒有可用緩沖區(qū)容納被發(fā)送的值時(shí),發(fā)送動(dòng)作才會阻塞。

這導(dǎo)致有緩沖的通道和無緩沖的通道之間的一個(gè)很大的不同:無緩沖的通道保證進(jìn)行發(fā)送和接收的 goroutine 會在同一時(shí)間進(jìn)行數(shù)據(jù)交換;有緩沖的通道沒有這種保證。

如果給定了一個(gè)緩沖區(qū)容量,通道就是異步的。只要緩沖區(qū)有未使用空間用于發(fā)送數(shù)據(jù),或還包含可以接收的數(shù)據(jù),那么其通信就會無阻塞地進(jìn)行。

可以通過內(nèi)置的close函數(shù)來關(guān)閉channel實(shí)現(xiàn)。

  • channel不像文件一樣需要經(jīng)常去關(guān)閉,只有當(dāng)你確實(shí)沒有任何發(fā)送數(shù)據(jù)了,或者你想顯式的結(jié)束range循環(huán)之類的,才去關(guān)閉channel;

  • 關(guān)閉channel后,無法向channel 再發(fā)送數(shù)據(jù)(引發(fā) panic 錯(cuò)誤后導(dǎo)致接收立即返回零值);

  • 關(guān)閉channel后,可以繼續(xù)向channel接收數(shù)據(jù),不能繼續(xù)發(fā)送數(shù)據(jù);

  • 對于nil channel,無論收發(fā)都會被阻塞。

本書《Go語言四十二章經(jīng)》內(nèi)容在github上同步地址:https://github.com/ffhelicopter/Go42
本書《Go語言四十二章經(jīng)》內(nèi)容在簡書同步地址: http://www.itdecent.cn/nb/29056963

雖然本書中例子都經(jīng)過實(shí)際運(yùn)行,但難免出現(xiàn)錯(cuò)誤和不足之處,煩請您指出;如有建議也歡迎交流。
聯(lián)系郵箱:roteman@163.com

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Chapter 8 Goroutines and Channels Go enable two styles of...
    SongLiang閱讀 1,739評論 0 3
  • 系統(tǒng)文件介紹 在程序啟動(dòng)運(yùn)行時(shí),自動(dòng)打開,運(yùn)行結(jié)束,自動(dòng)關(guān)閉。 鍵盤(硬件)—— 標(biāo)準(zhǔn)輸入(文件)stdin —...
    泡泡龍吐泡泡閱讀 5,120評論 0 2
  • Go入門 Go介紹 部落圖鑒之Go:爹好還這么努力? 環(huán)境配置 安裝 下載源碼編譯安裝 下載相應(yīng)平臺的安裝包安裝 ...
    齊天大圣李圣杰閱讀 4,771評論 0 26
  • Go語言并發(fā)模型 Go 語言中使用了CSP模型來進(jìn)行線程通信,準(zhǔn)確說,是輕量級線程goroutine之間的通信。C...
    副班長國偉閱讀 2,203評論 0 2
  • 一。 卯時(shí)一刻睡起。苦於天熱。開門窗。室友劉君怒。實(shí)向日擾我清夢者。不理。待吾閑時(shí)。當(dāng)為其作《好夢為室友所壞歌》。...
    仲玙閱讀 361評論 1 2

友情鏈接更多精彩內(nèi)容