golang 無(wú)緩存channel和有緩存channel

golang 無(wú)緩存channel和有緩存channel

無(wú)緩存通道

var ch = make(chan int)  // 創(chuàng)建一個(gè)int類(lèi)型的channel
cap(ch)                  // ch的容量是0
  • 發(fā)送/存入

    ch <- 1  // 存入一個(gè)int類(lèi)型的值
    
  • 接收/取出

    x := <-ch  // 取出ch中的值潦牛,并賦值給x
    
  • 關(guān)閉

    close(ch) // 關(guān)閉發(fā)送方channel眶掌,對(duì)接收發(fā)channel關(guān)閉操作會(huì)panic
    val, ok := <-ch // ok 可用于判斷通道是否關(guān)閉。
    
  

| 操作  |  nil  |       closed channel       |
| :---: | :---: | :------------------------: |
| close | panic |           panic            |
| 存入  | 阻塞  |           panic            |
| 取出  | 阻塞  | 返回對(duì)應(yīng)類(lèi)型零值和關(guān)閉信號(hào) |

了解了channel的基本語(yǔ)法巴碗,我們看一個(gè)例子

```go
func main() {
    ch := make(chan int) // 創(chuàng)建一個(gè)無(wú)緩存channel
    ch <- 233
    fmt.Println(<-ch)
}

上面的代碼可以成功執(zhí)行嗎朴爬?答案是否定的。會(huì)出現(xiàn)死鎖橡淆。這是因?yàn)?ch是一個(gè)無(wú)緩存的channel召噩,當(dāng)我們向它存入值的時(shí)候,會(huì)阻塞所在goroutine逸爵,這里當(dāng)然是main.main的goroutine蚣常,后面的語(yǔ)句根本不會(huì)執(zhí)行到。那么我們應(yīng)該怎么修改呢痊银,繼續(xù)看代碼:

func main() {
    ch := make(chan int)
    go func(chan int) { ch <- 233 }(ch)
    fmt.Println(<-ch)
}

這里我們?cè)趍ain.main 主goroutine 中新建了一個(gè)goroutine抵蚊,這樣阻塞的就是這個(gè)goroutine了,不影響main.main溯革。當(dāng)主協(xié)程執(zhí)行到<-ch的時(shí)候贞绳,取出值,完成一次同步通信致稀。

一句話(huà)總結(jié):無(wú)緩存的channel只有在receiver準(zhǔn)備好后send才被執(zhí)行冈闭,也就是說(shuō)receiver的goroutine 和sender的goroutine 必須成對(duì)出現(xiàn)

之前看到一個(gè)打網(wǎng)球的模擬,很有趣抖单,可以加深一下理解萎攒,我們看一下

package main

import(
    "math/rand"
    "sync"
    "time"
    "fmt"
)

// wg用來(lái)等待程序結(jié)束
var wg sync.WaitGroup

func init() {
    rand.Seed(time.Now().UnixNano())
}

// main是所有Go程序的入口
func main() {
    // 創(chuàng)建一個(gè)無(wú)緩沖的通道
    court := make(chan int)

    // 計(jì)數(shù)加2,表示要等待兩個(gè)goroutine
    wg.Add(2)

    // 啟動(dòng)兩個(gè)選手
    go player("Nick", court)
    go player("Jack", court)

    // 發(fā)球
    court <- 1

    // 等待游戲結(jié)束
    wg.Wait()
}

// player 模擬一個(gè)選手在打網(wǎng)球
func player(name string, court chan int) {
    // 在函數(shù)退出時(shí)調(diào)用Done來(lái)通知main函數(shù)工作已經(jīng)完成
    defer wg.Done()

    for{
        // 等待球被擊打過(guò)來(lái)
        ball, ok := <-court
        if !ok {
            // 如果通道被關(guān)閉矛绘,我們就贏了
            fmt.Printf("Player %s Won\n", name)
            return
        }

        // 選隨機(jī)數(shù)耍休,然后用這個(gè)數(shù)來(lái)判斷我們是否丟球
        n := rand.Intn(100)
        if n%5 == 0 {
            fmt.Printf("Player %s Missed\n", name)

            // 關(guān)閉通道,表示我們輸了
            close(court)
            return
        }

        // 顯示擊球數(shù)货矮,并將擊球數(shù)加1
        fmt.Printf("Player %s Hit %d\n", name, ball)
        ball++

        // 將球打向?qū)κ?        court <- ball
    }
}
E:\go\src\learngolang\00test>go run main.go
Player Jack Hit 1
Player Nick Hit 2
Player Jack Hit 3
Player Nick Hit 4
Player Jack Hit 5
Player Nick Hit 6
Player Jack Hit 7
Player Nick Hit 8
Player Jack Hit 9
Player Nick Missed
Player Jack Won

上面的go函數(shù)為什么能交替執(zhí)行呢羊精?首先,我們有main主協(xié)程g0囚玫,里面創(chuàng)建了兩個(gè)goroutine喧锦,g1,g2抓督,然后燃少,主協(xié)程g0向無(wú)緩存channel中發(fā)送數(shù)據(jù),g0阻塞铃在,這時(shí)阵具,g1、g2中某一個(gè),如g2怔昨,先進(jìn)入通道的接收隊(duì)列,則通道獲得一次同步宿稀,g0取消阻塞趁舀。在player函數(shù)中court <- ball 這句代碼繼續(xù)向通道發(fā)送數(shù)據(jù),g2阻塞祝沸,這時(shí)通道中g(shù)1等待接收矮烹,g1開(kāi)始執(zhí)行,通道接收操作之后罩锐,g2取消阻塞奉狈,for循環(huán)繼續(xù)等待接收,阻塞涩惑,g1最后向通道發(fā)送數(shù)據(jù)仁期,g1阻塞;g2接收數(shù)據(jù)....直到goroutine結(jié)束運(yùn)行竭恬。

WaitGroup是用來(lái)讓g0等待g1跛蛋、g2執(zhí)行的。因?yàn)楫?dāng)程序執(zhí)行到一條go語(yǔ)句時(shí)痊硕,Go語(yǔ)言的運(yùn)行時(shí)系統(tǒng)赊级,會(huì)試圖從某個(gè)存放空閑的G隊(duì)列中獲取一個(gè)G(也就是goroutine),沒(méi)有則創(chuàng)建一個(gè)新的G岔绸。Go語(yǔ)言運(yùn)行時(shí)系統(tǒng)會(huì)用這個(gè)G去包裝當(dāng)前的那個(gè)go函數(shù)(或者說(shuō)該函數(shù)中的那些代碼)理逊,然后再把這個(gè)G追加到某個(gè)存放可運(yùn)行的G的隊(duì)列中。這類(lèi)隊(duì)列中的G總是會(huì)按照先入先出的順序盒揉,很快地由運(yùn)行時(shí)系統(tǒng)內(nèi)部的調(diào)度器安排運(yùn)行晋被。雖然這會(huì)很快,但是由于上面所說(shuō)的那些準(zhǔn)備工作還是不可避免的刚盈,所以耗時(shí)還是存在的墨微。因此,go函數(shù)的執(zhí)行時(shí)間總是會(huì)明顯滯后于它所屬的go語(yǔ)句的執(zhí)行時(shí)間扁掸。不然翘县,主goroutine代碼執(zhí)行完關(guān)閉,其創(chuàng)建的其他goroutine還沒(méi)來(lái)得及執(zhí)行也就跟著關(guān)閉了谴分。

有緩存channel

var ch = make(chan int, 10) //創(chuàng)建一個(gè)有緩存的channel锈麸,容量是10,即可以傳入10個(gè)int類(lèi)型的值
  • 發(fā)送

  • 接收

  • 關(guān)閉

    func main() {
      ch := make(chan int, 10)
      for i := 0; i < 5; i++ {
          ch <- i + 1            // 向channel中傳入5 個(gè)值
      }
      fmt.Println(len(ch))        // 5      // channel長(zhǎng)度為5
      close(ch)                   // 關(guān)閉channel
      fmt.Println("--------------")
    
      for x := range ch {         // range的截止條件是channel關(guān)閉牺蹄,即關(guān)閉前存入多少個(gè)值忘伞,range的范圍是多少,如果range的對(duì)象沒(méi)有關(guān)閉,則最終阻塞氓奈,引起panic
          fmt.Println(x)          // 接收一個(gè)翘魄,channel的有效值少一個(gè)
      }
      fmt.Println("--------------")
      fmt.Println(len(ch))        // 0       // channel 長(zhǎng)度為0
      fmt.Println(cap(ch))        // 10      // channel容量為10
    }
    
操作 nil 滿(mǎn) closed channel
close panic 關(guān)閉 關(guān)閉 panic
存入 阻塞 阻塞 成功發(fā)送 panic
取出 阻塞 成功接收 阻塞 空:返回類(lèi)型零值、false<br />非空:返回相應(yīng)值舀奶、true

buffer沒(méi)滿(mǎn)則暑竟,寫(xiě)入\發(fā)送 不會(huì)阻塞;buffer不空育勺,則讀取\接收 不會(huì)阻塞但荤。發(fā)送和接收不是同步的。但多說(shuō)一句涧至,其實(shí)這種情況不太常用腹躁,原因也很簡(jiǎn)單。因?yàn)?strong>上下游的消費(fèi)情況是統(tǒng)一的南蓬,如果生產(chǎn)者生產(chǎn)的速度過(guò)快纺非,而消費(fèi)端跟不上的話(huà),即使把它先暫存在緩沖區(qū)當(dāng)中也沒(méi)什么用赘方,早晚還是會(huì)要阻塞的铐炫。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市蒜焊,隨后出現(xiàn)的幾起案子倒信,更是在濱河造成了極大的恐慌,老刑警劉巖泳梆,帶你破解...
    沈念sama閱讀 222,104評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鳖悠,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡优妙,警方通過(guò)查閱死者的電腦和手機(jī)乘综,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)套硼,“玉大人卡辰,你說(shuō)我怎么就攤上這事⌒耙猓” “怎么了九妈?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,697評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀(guān)的道長(zhǎng)雾鬼。 經(jīng)常有香客問(wèn)我萌朱,道長(zhǎng),這世上最難降的妖魔是什么策菜? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,836評(píng)論 1 298
  • 正文 為了忘掉前任晶疼,我火速辦了婚禮酒贬,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘翠霍。我一直安慰自己锭吨,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布寒匙。 她就那樣靜靜地躺著零如,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蒋情。 梳的紋絲不亂的頭發(fā)上埠况,一...
    開(kāi)封第一講書(shū)人閱讀 52,441評(píng)論 1 310
  • 那天耸携,我揣著相機(jī)與錄音棵癣,去河邊找鬼。 笑死夺衍,一個(gè)胖子當(dāng)著我的面吹牛狈谊,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播沟沙,決...
    沈念sama閱讀 40,992評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼河劝,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了矛紫?” 一聲冷哼從身側(cè)響起赎瞎,我...
    開(kāi)封第一講書(shū)人閱讀 39,899評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎颊咬,沒(méi)想到半個(gè)月后务甥,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,457評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡喳篇,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評(píng)論 3 341
  • 正文 我和宋清朗相戀三年敞临,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片麸澜。...
    茶點(diǎn)故事閱讀 40,664評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡挺尿,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出炊邦,到底是詐尸還是另有隱情编矾,我是刑警寧澤,帶...
    沈念sama閱讀 36,346評(píng)論 5 350
  • 正文 年R本政府宣布馁害,位于F島的核電站洽沟,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏蜗细。R本人自食惡果不足惜裆操,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評(píng)論 3 334
  • 文/蒙蒙 一怒详、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧踪区,春花似錦昆烁、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,511評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至传泊,卻和暖如春鼠渺,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背眷细。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,611評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工拦盹, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人溪椎。 一個(gè)月前我還...
    沈念sama閱讀 49,081評(píng)論 3 377
  • 正文 我出身青樓普舆,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親校读。 傳聞我的和親對(duì)象是個(gè)殘疾皇子沼侣,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容