Go中的Channel

復(fù)習(xí)下Golang中的Channel學(xué)習(xí)使我快樂

譯自Channels in Go钞速,該文章實(shí)際分兩部分奥溺,下一部分是Channels in Go - range and select,譯文為Go中的Channel——range和select吏够,同樣在我的Golang文集中

goroutine允許我們并行的運(yùn)行一些代碼。但是要想讓這些代碼對我們來說更有意義恍涂,我們會有一些額外的需求--我們應(yīng)該能夠傳遞數(shù)據(jù)到正在運(yùn)行的進(jìn)程中;當(dāng)并行的進(jìn)程成功產(chǎn)生數(shù)據(jù)時植榕,我們應(yīng)該能從該進(jìn)程中獲取到數(shù)據(jù)再沧。channel配合goroutine提供了實(shí)現(xiàn)這些需求的途徑

channel可以想象為一個指定了大小和容量的管道或傳送帶。我們可以在其一邊放置內(nèi)容尊残,然后在另一邊獲取內(nèi)容


圖示channel.png

我們采用一個蛋糕制作裝箱工廠的例子來進(jìn)行下面的說明炒瘸。我們有一臺用于制作蛋糕的機(jī)器,還有一臺用于裝箱的機(jī)器寝衫。她們通過一條傳送帶互相連接--蛋糕機(jī)將蛋糕放上傳送帶顷扩,裝箱機(jī)在發(fā)現(xiàn)傳送帶上么有蛋糕時將其取走并裝入箱子

在go中,chan關(guān)鍵字用于定義一個channel慰毅。make關(guān)鍵字用于創(chuàng)建cahnnel隘截,創(chuàng)建時指定channel傳遞的數(shù)據(jù)類型

示例代碼1
ic := make(chan int) //a channel that can send and receive an int
sc := make(chan string) //a channel hat can send and receive a string
myc := make (chan my_type) //a channel for a custom defined struct type

在channel的變量名前面或后面,你可以使用<-操作符來指示channel用于發(fā)送還是接收數(shù)據(jù)(注意對應(yīng)關(guān)系).假設(shè)事富,my_channel是一個接收int類型數(shù)據(jù)的channel技俐,你可以像my_channel <- 5這樣向其發(fā)送數(shù)據(jù)乘陪,并且你可以像my_recvd_value <- my_channel這樣來從中接收收據(jù)

想象channel是一個有方向的傳送帶:
從外部指向channel的箭頭用于向channel放置數(shù)據(jù)
從channel指向外部的箭頭用于從channel獲取數(shù)據(jù)

示例代碼2
my_channel := make(chan int)

//within some goroutine - to put a value on the channel
my_channel <- 5 

//within some other goroutine - to take a value off the channel
var my_recvd_value int
my_recvd_value = <- my_channel

當(dāng)然统台,你也可以指定channel中的數(shù)據(jù)移動方向,只需要在創(chuàng)建channel時在chan關(guān)鍵字旁使用<-指明方向

示例代碼3
ic_send_only := make (<-chan int) //a channel that can only send data - arrow going out is sending
ic_recv_only := make (chan<- int) //a channel that can only receive a data - arrow going in is receiving 

channel能夠保有的數(shù)據(jù)個數(shù)很重要啡邑。她能指示具體有多少條數(shù)據(jù)可以同時工作贱勃。即使發(fā)送者有能力產(chǎn)生很多條目,如果接受者沒有能力接收她們谤逼,那么她們就不能工作贵扰。這將會有很多蛋糕從傳送帶上掉落并浪費(fèi)掉ORZ。在并行計算中流部,這叫做生產(chǎn)者-消費(fèi)者同步問題(producer-consumer synchronization problem)

如果channel的容量(capacity)是1——也就是說戚绕,一旦有數(shù)據(jù)被放入channel,那么該數(shù)據(jù)必須被取走才能讓另一條數(shù)據(jù)放入枝冀,這就是同步channel(synchronous channel)舞丛。channel的每一邊——發(fā)送者和接受者——在同一時間只交流一條數(shù)據(jù),然后必須等待果漾,直到另一邊完成了相應(yīng)的發(fā)送或接收動作

目前為止球切,我們定義的所有的channel默認(rèn)都是同步channel,也就是說绒障,一條數(shù)據(jù)被放入channel后必須被取走才能再放置另一條數(shù)據(jù)《执眨現(xiàn)在,我們完成上面提到的蛋糕制作裝箱工廠户辱。由于channel在不同的goroutine之間交流數(shù)據(jù)鸵钝,我們有兩個名為makeCakeAndSendreceiveCakeAndPack的函數(shù)糙臼。每個函數(shù)都接收一個channel的引用作為參數(shù),這樣它們可以通過該channel進(jìn)行交流

示例代碼4
package main

import (
    "fmt"
    "time"
    "strconv"
)

var i int

func makeCakeAndSend(cs chan string) {
    i = i + 1
    cakeName := "Strawberry Cake " + strconv.Itoa(i)
    fmt.Println("Making a cake and sending ...", cakeName)
    cs <- cakeName //send a strawberry cake
}

func receiveCakeAndPack(cs chan string) {
    s := <-cs //get whatever cake is on the channel
    fmt.Println("Packing received cake: ", s)
}

func main() {
    cs := make(chan string)
    for i := 0; i<3; i++ {
        go makeCakeAndSend(cs)
        go receiveCakeAndPack(cs)

        //sleep for a while so that the program doesn’t exit immediately and output is clear for illustration
        time.Sleep(1 * 1e9)
    }
}
輸出結(jié)果
Making a cake and sending ... Strawberry Cake 1 
Packing received cake: Strawberry Cake 1 
Making a cake and sending ... Strawberry Cake 2 
Packing received cake: Strawberry Cake 2 
Making a cake and sending ... Strawberry Cake 3 
Packing received cake: Strawberry Cake 3

在上述代碼中恩商,我們創(chuàng)建了三個制作蛋糕的函數(shù)調(diào)用弓摘,并在其之后立刻創(chuàng)建了三個裝箱蛋糕的函數(shù)調(diào)用。我們知道痕届,每當(dāng)一個蛋糕被裝箱韧献,就會有另一個蛋糕同時被制作并準(zhǔn)備好被裝箱。當(dāng)然如果你吹毛求疵研叫,代碼中確實(shí)有一個很輕微的含混之處——在打印Making a cake and sending …和實(shí)際發(fā)送蛋糕到channel之間有延時锤窑。代碼中我們在每個循環(huán)中調(diào)用了time.Sleep(),用于讓制作和裝箱動作一個接一個的發(fā)生嚷炉,這樣做是正確的渊啰。由于我們的channel是同步的,而且同一時間僅支持一條數(shù)據(jù)申屹,一個從channel中移除蛋糕的動作也就是一個裝箱動作绘证,必須在制作新蛋糕并將其放入channel之前發(fā)生

現(xiàn)在我們改動下上面的內(nèi)容讓其更像我們正常使用的代碼。典型的goroutine一般是一個包含了不斷循環(huán)的內(nèi)容的代碼塊哗讥,其內(nèi)部完成一些操作并且與其他的goroutine通過channel交換數(shù)據(jù)嚷那。在下面的例子中,我們將循環(huán)移至goroutine函數(shù)內(nèi)部杆煞,然后我們僅調(diào)用該goroutine一次

示例代碼5
package main                                                                                                                                                           

import (
    "fmt"
    "time"
    "strconv"
)

func makeCakeAndSend(cs chan string) {
    for i := 1; i<=3; i++ {
        cakeName := "Strawberry Cake " + strconv.Itoa(i)
        fmt.Println("Making a cake and sending ...", cakeName)
        cs <- cakeName //send a strawberry cake
    }   
}

func receiveCakeAndPack(cs chan string) {
    for i := 1; i<=3; i++ {
        s := <-cs //get whatever cake is on the channel
        fmt.Println("Packing received cake: ", s)
    }   
}

func main() {
    cs := make(chan string)
    go makeCakeAndSend(cs)
    go receiveCakeAndPack(cs)

    //sleep for a while so that the program doesn’t exit immediately
    time.Sleep(4 * 1e9)
}
輸出結(jié)果
Making a cake and sending ... Strawberry Cake 1 
Making a cake and sending ... Strawberry Cake 2 
Packing received cake: Strawberry Cake 1 
Packing received cake: Strawberry Cake 2 
Making a cake and sending ... Strawberry Cake 3 
Packing received cake: Strawberry Cake 3

輸出結(jié)果在我電腦上是這樣的魏宽,在你電腦上可能會不同,輸出結(jié)果依賴于你機(jī)器上goroutine的執(zhí)行順序决乎。如前所述队询,我們僅調(diào)用了每個goroutine一次,并且傳遞了一個公有的channel給她們构诚。在每個goroutine內(nèi)部有三個循環(huán)蚌斩,makeCakeAndSend將蛋糕放入channel,receiveCakeAndPack將蛋糕從channel中取出范嘱。由于程序會在我們創(chuàng)建了兩個goroutine后立即結(jié)束送膳,因此我們必須手動增加一個時間暫停操作來讓三個蛋糕都被制作和裝箱好

極其重要的一點(diǎn)是,我們必須理解彤侍,上面的輸出并沒有正確的反應(yīng)channel中實(shí)際的發(fā)送和接收操作肠缨。發(fā)送和接收在這里是同步的——同一時間僅有一個蛋糕。然而由于在打印語句和實(shí)際發(fā)送與接收間的延時盏阶,輸出看起來在順序上是錯誤的晒奕。而實(shí)際上發(fā)生的是:

Making a cake and sending ... Strawberry Cake 1 
Packing received cake: Strawberry Cake 1 
Making a cake and sending ... Strawberry Cake 2 
Packing received cake: Strawberry Cake 2 
Making a cake and sending ... Strawberry Cake 3 Packing received cake: Strawberry Cake 3

因此,一定要記住,在處理goroutine和channel時脑慧,通過打印日志分析執(zhí)行順序一定要萬分小心

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末魄眉,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子闷袒,更是在濱河造成了極大的恐慌坑律,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件囊骤,死亡現(xiàn)場離奇詭異晃择,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)也物,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進(jìn)店門宫屠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人滑蚯,你說我怎么就攤上這事浪蹂。” “怎么了告材?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵坤次,是天一觀的道長。 經(jīng)常有香客問我斥赋,道長缰猴,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任灿渴,我火速辦了婚禮洛波,結(jié)果婚禮上胰舆,老公的妹妹穿的比我還像新娘骚露。我一直安慰自己,他們只是感情好缚窿,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布棘幸。 她就那樣靜靜地躺著,像睡著了一般倦零。 火紅的嫁衣襯著肌膚如雪误续。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天扫茅,我揣著相機(jī)與錄音蹋嵌,去河邊找鬼。 笑死葫隙,一個胖子當(dāng)著我的面吹牛栽烂,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼腺办,長吁一口氣:“原來是場噩夢啊……” “哼焰手!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起怀喉,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤书妻,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后躬拢,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體躲履,經(jīng)...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年聊闯,在試婚紗的時候發(fā)現(xiàn)自己被綠了崇呵。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,577評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡馅袁,死狀恐怖域慷,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情汗销,我是刑警寧澤犹褒,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站弛针,受9級特大地震影響叠骑,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜削茁,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一宙枷、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧茧跋,春花似錦慰丛、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至粥烁,卻和暖如春贤笆,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背讨阻。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工芥永, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人钝吮。 一個月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓埋涧,卻偏偏與公主長得像贴唇,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子飞袋,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評論 2 348

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

  • 譯自Channels in Go - range and select戳气,該文章分為兩部分,第一部分的翻譯見Go中的...
    Kenshinsyrup閱讀 40,613評論 3 15
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理巧鸭,服務(wù)發(fā)現(xiàn)瓶您,斷路器,智...
    卡卡羅2017閱讀 134,628評論 18 139
  • Go的內(nèi)存模型 看完這篇文章你會明白 一個Go程序在啟動時的執(zhí)行順序 并發(fā)的執(zhí)行順序 并發(fā)環(huán)境下如何保證數(shù)據(jù)的同步...
    初級賽亞人閱讀 2,841評論 0 2
  • Goroutine是Go里的一種輕量級線程——協(xié)程纲仍。相對線程呀袱,協(xié)程的優(yōu)勢就在于它非常輕量級,進(jìn)行上下文切換的代價非...
    witchiman閱讀 4,820評論 0 9
  • 本文翻譯自Sameer Ajmani的文章《Go Concurrency Patterns: Pipelines ...
    大蟒傳奇閱讀 3,862評論 0 15