Go語(yǔ)言channel備忘錄

目錄

  1. 無緩沖channel等價(jià)于緩沖大小為0的channel,而不是1
  2. 發(fā)送者和接收者哪些情況會(huì)阻塞
  3. close哪些情況會(huì)導(dǎo)致panic
  4. 如何優(yōu)雅的關(guān)閉channel
  5. 當(dāng)一個(gè)select中有多個(gè)channel滿足可讀時(shí),誰被激活
  6. select with default
  7. 讀取時(shí)獲取第二個(gè)返回值,以此判斷該channel是否被關(guān)閉
  8. close前寫入的數(shù)據(jù),接收者依然可以按順序讀取到
  9. 一個(gè)channel有多個(gè)接收者時(shí),close channel會(huì)喚醒所有接收者
  10. 配合timer實(shí)現(xiàn)channel讀取的超時(shí)機(jī)制
  11. 當(dāng)channel只用做同步通知,不關(guān)心channel中傳輸?shù)闹禃r(shí),可使用 chan struct{} 類型
  12. 單向channel類型的作用
  13. 可以make單向channel,但是這樣做沒有意義
  14. channel配合for range的簡(jiǎn)化寫法
1. 無緩沖channel等價(jià)于緩沖大小為0的channel,而不是1
var ch chan int // ch == nil

// 創(chuàng)建無緩沖channel
ch := make(chan int) // ch != nil
// 等價(jià)于
// ch := make(chan int, 0)
// 不等價(jià)于
// ch := make(chan int, 1)

close(ch) // close執(zhí)行后, ch != nil
2. 發(fā)送者和接收者哪些情況會(huì)阻塞
  • 往值為nil的channel發(fā)送數(shù)據(jù): 永久阻塞
  • 從值為nil的channel讀取數(shù)據(jù): 永久阻塞
  • 無緩沖模式的發(fā)送者: 阻塞直到數(shù)據(jù)被接收者接收
  • 無緩沖模式的接收者: 無數(shù)據(jù)可讀時(shí),阻塞
  • 有緩沖模式的發(fā)送者: 當(dāng)緩沖滿時(shí),阻塞
  • 有緩沖模式的接收者: 無數(shù)據(jù)可讀時(shí),阻塞
3. close哪些情況會(huì)導(dǎo)致panic
  1. close值為nil的channel
  2. close已經(jīng)被close的channel
  3. 向已經(jīng)被close的channel發(fā)送數(shù)據(jù)
4. 如何優(yōu)雅的關(guān)閉channel

需要特別注意:

  1. 接收者關(guān)閉channel要小心,因?yàn)殛P(guān)閉后發(fā)送者繼續(xù)發(fā)送會(huì)panic
  2. 當(dāng)有多個(gè)發(fā)送者時(shí),在一個(gè)發(fā)送者協(xié)程中關(guān)閉channel要小心,因?yàn)殛P(guān)閉后其他發(fā)送者繼續(xù)發(fā)送會(huì)panic

復(fù)雜情況下的參考思路:

  1. channel的關(guān)閉并非必須的,只要channel對(duì)象不再被持有,垃圾回收器會(huì)清理它
  2. 可使用原子變量等同步原語(yǔ)保證close有且只有發(fā)生一次
  3. 除了傳輸數(shù)據(jù)的channel,可以再增加channel配合select使用,用于取消生產(chǎn)、消費(fèi)
  4. 接收端也可以通過其他channel發(fā)出消息,反向通知發(fā)送端
5. 當(dāng)一個(gè)select中有多個(gè)channel滿足可讀時(shí),誰被激活

Go隨機(jī)選取一個(gè)滿足條件的case分支執(zhí)行,而不是按代碼順序選取。

// 如下代碼段,可能輸出ch1,也可能輸出ch2
ch1 := make(chan int, 8)
ch2 := make(chan int, 8)
ch1 <- 1
ch2 <- 1
select {
case <- ch1:
    fmt.Println("ch1")
case <- ch2:
    fmt.Println("ch2")
}
6. select with default

當(dāng)select中的條件都不滿足時(shí),會(huì)立即執(zhí)行default分支

// 如下代碼段,會(huì)立即打印default
ch1 := make(chan int, 1)
ch2 := make(chan int)
select {
case <- ch1:
    fmt.Println("ch1")
case <- ch2:
    fmt.Println("ch2")
default:
    fmt.Println("default")
}
7. 讀取時(shí)獲取第二個(gè)返回值,以此判斷該channel是否被關(guān)閉
v, ok := <- ch
// 當(dāng)channel被關(guān)閉后,v為channel類型的零值,ok為false
8. close前寫入的數(shù)據(jù),接收者依然可以按順序讀取到
ch := make(chan int, 8)
ch <- 1
ch <- 2
ch <- 3
close(ch)

for {
    v, ok := <- ch
    fmt.Println(v, ok)
    if !ok {
        break
    }
}
// 以上代碼段,將打印出如下結(jié)果:
// 1 true
// 2 true
// 3 true
// 0 false
9. 一個(gè)channel有多個(gè)接收者時(shí),close channel會(huì)喚醒所有接收者
10. 配合timer實(shí)現(xiàn)channel讀取的超時(shí)機(jī)制

但在高性能場(chǎng)景需要注意,參見: golang源碼閱讀之定時(shí)器以及避坑指南

11. 當(dāng)channel只用做同步通知,不關(guān)心channel中傳輸?shù)闹禃r(shí),可使用 chan struct{} 類型

好處是語(yǔ)意上更正確,代碼可讀性更高。

// 初始化
ch := make(chan struct{}, 1)

// 寫入
ch <- chan struct{}{}
12. 單向channel類型的作用
// 比如Go系統(tǒng)庫(kù)中Timer的實(shí)現(xiàn),就使用了只讀類型的channel,
// 目的是限制該channel在上層Timer對(duì)象只能讀,不能寫。這種限制是編譯期的

// 提供給用戶的Timer對(duì)象
type Timer struct {
    C <-chan Time // 只讀類型
    r runtimeTimer
}

// 實(shí)際make的是chan Time類型
func NewTimer(d Duration) *Timer {
    c := make(chan Time, 1)
    t := &Timer{
        C: c, // chan Time轉(zhuǎn)換成只讀類型,后續(xù)通過Timer對(duì)象訪問C數(shù)據(jù)成員的操作只能讀,不能寫
        r: runtimeTimer{
            when: when(d),
            f:    sendTime,
            arg:  c, // 將chan Time類型傳遞給底層runtimeTimer中使用,底層可以寫
        },
    }
    startTimer(&t.r)
    return t
}

// 用戶調(diào)用After時(shí),返回只讀類型的channel
func After(d Duration) <-chan Time {
    return NewTimer(d).C
}
13. 可以make單向channel,但是這樣做沒有意義
// 以下初始化了一個(gè)只寫的channel是合法的,但是只能寫,不能讀,應(yīng)該沒有這種使用場(chǎng)景
ch := make(chan<- int, 4)
14. channel配合for range的簡(jiǎn)化寫法
ch := make(chan int, 4)
ch <- 1
ch <- 2
close(ch)
for v := range ch {
    fmt.Println(v)
}
fmt.Println("< for")
// 以上代碼段將打印如下結(jié)果
// 1
// 2
// < for

參考鏈接

備忘錄類型文章,后續(xù)的修改會(huì)第一時(shí)間在我的個(gè)人站點(diǎn)更新,地址: https://pengrl.com/p/23102

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

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

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