Channel最佳實踐之基本規(guī)則【譯】

channel[通道]是golang的一種重要特性,正是因為channel的存在才使得golang不同于其它語言。channel使得并發(fā)編程變得簡單容易有趣。

channel的概念和語法

一個channel可以理解為一個先進先出的消息隊列巡莹。channel用來在協(xié)程[goroutine]之間傳遞數(shù)據(jù)泰讽,準確的說,是用來傳遞數(shù)據(jù)的所有權(quán)薪贫。一個設計良好的程序應該確保同一時刻channel里面的數(shù)據(jù)只會被同一個協(xié)程擁有恍箭,這樣就可以避免并發(fā)帶來的數(shù)據(jù)不安全問題[data races]。

channel的類型

像數(shù)組瞧省、切片和字典一樣扯夭,channel類型是一種組合類型,每一種channel類型都對應著一種簡單的數(shù)據(jù)類型鞍匾。比如元素的類型是string交洗,那么對應的channel類型就是chan string,進入channel的數(shù)據(jù)也就必須是string類型的值橡淑。

官方的go編譯器限制channel最多能容納到65535個元素构拳,盡管如此,我們也不應該傳遞體積過大的元素值梁棠,因為channel的數(shù)據(jù)從進入到流出會涉及到數(shù)據(jù)拷貝操作置森。如果元素體積過大,最好的方法還是使用傳遞指針來取代傳遞值符糊。

channel類型是可以帶有方向的凫海,假設T是一種類型

chan T是雙向channel類型,編譯器允許對雙向channel同時進行發(fā)送和接收男娄。

chan<- T是只寫channel類型盐碱,編譯器只允許往channel里面發(fā)送數(shù)據(jù)。

<-chan T是只讀channel類型沪伙,編輯器只允許從channel里面接收數(shù)據(jù)瓮顽。

雙向類型的channel,可以被強制轉(zhuǎn)換成只讀channel或者是只寫channel围橡,但是反過來卻不行暖混,只讀和只寫channel是不可以轉(zhuǎn)換成雙向channel的。

channel類型的零值形式稱為空channel翁授。一個非空channel類型必須通過make關(guān)鍵字進行創(chuàng)建拣播。例如make(chan int, 10)將會創(chuàng)建出一個可以容納10個int值的channel晾咪。第二個整形的參數(shù)值代表的就是channel可以容納數(shù)據(jù)的大小,如果不提供這個參數(shù)值贮配,那默認值就是零谍倦。

varchchanstring;// nil channel
ch:=make(chanstring);// zero channel
ch:=make(chanstring,10);// buffered channel

channel里面的value buffer的容量也就是channel的容量。channel的容量為零表示這是一個阻塞型通道泪勒,非零表示緩沖型通道[非阻塞型通道]昼蛀。

channel內(nèi)部結(jié)構(gòu)

每個channel內(nèi)部實現(xiàn)都有三個隊列

接收消息的協(xié)程隊列。這個隊列的結(jié)構(gòu)是一個限定最大長度的鏈表圆存,所有阻塞在channel的接收操作的協(xié)程都會被放在這個隊列里叼旋。

發(fā)送消息的協(xié)程隊列。這個隊列的結(jié)構(gòu)也是一個限定最大長度的鏈表沦辙。所有阻塞在channel的發(fā)送操作的協(xié)程也都會被放在這個隊列里夫植。

環(huán)形數(shù)據(jù)緩沖隊列。這個環(huán)形數(shù)組的大小就是channel的容量油讯。如果數(shù)組裝滿了详民,就表示channel滿了,如果數(shù)組里一個值也沒有陌兑,就表示channel是空的沈跨。對于一個阻塞型channel來說,它總是同時處于即滿又空的狀態(tài)诀紊。

一個channel被所有使用它的協(xié)程所引用,也就是說隅俘,只要這兩個裝了協(xié)程的隊列長度大于零邻奠,那么這個channel就永遠不會被垃圾回收。另外为居,協(xié)程本身如果阻塞在channel的讀寫操作上碌宴,這個協(xié)程也永遠不會被垃圾回收,即使這個channel只會被這一個協(xié)程所引用蒙畴。

channel的使用

channel支持以下操作

使用cap(ch)函數(shù)查詢channel的容量贰镣,cap是golang的內(nèi)置函數(shù)

使用len(ch)函數(shù)查詢channel內(nèi)部的數(shù)據(jù)長度,len函數(shù)也是內(nèi)置的膳凝,表面上這個函數(shù)很有意義碑隆,但實際上它很少用。

使用close(ch)關(guān)閉channel蹬音,close也是內(nèi)置函數(shù)上煤。一個非空channel只能夠被關(guān)閉一次,如果關(guān)閉一個已經(jīng)被關(guān)閉的或者是關(guān)閉一個空channel將會引發(fā)panic著淆。另外關(guān)閉一個只讀channel是非法的劫狠,編譯器直接報錯拴疤。

使用ch <- v發(fā)送一個值v到channel。發(fā)送值到channel可能會有多種結(jié)果独泞,即可能成功呐矾,也可能阻塞,甚至還會引發(fā)panic懦砂,取決于當前channel在什么狀態(tài)蜒犯。

使用 v, ok <- ch 接收一個值。第二個遍歷ok是可選的孕惜,它表示channel是否已關(guān)閉愧薛。接收值只會又兩種結(jié)果,要么成功要么阻塞衫画,而永遠也不會引發(fā)panic毫炉。

所有的這些操作都是同步的協(xié)程安全的,不需要加任何其它同步控制削罩。

For-Range

for-range語法可以用到通道上瞄勾。循環(huán)會一直接收channel里面的數(shù)據(jù),直到channel關(guān)閉弥激。不同于array/slice/map上的for-range进陡,channel的for-range只允許有一個變量。

for v = range aChannel{
? ? // use v
}
等價于
for{
? ? v,ok=<-aChannel
? ? if!ok{
? ? ? ? break
? ? }
? ? // use v
}


注意微服,for-range對應的channel不能是只寫channel趾疚。

Select-Cases

select塊是為channel特殊設計的語法,它和switch語法非常相近以蕴。分支上它們都可以有多個case塊和做多一個default塊糙麦,但是也有很多不同

select 到 括號{之間不得有任何表達式

fallthrough關(guān)鍵字不能用在select里面

所有的case語句要么是channel的發(fā)送操作,要么就是channel的接收操作

select里面的case語句是隨機執(zhí)行的丛肮,而不能是順序執(zhí)行的赡磅。設想如果第一個case語句對應的channel是非阻塞的話,case語句的順序執(zhí)行會導致后續(xù)的case語句一直得不到執(zhí)行除非第一個case語句對應的channel里面的值都耗盡了宝与。

如果所有case語句關(guān)聯(lián)的操作都是阻塞的焚廊,default分支就會被執(zhí)行。如果沒有default分支习劫,當前goroutine就會阻塞咆瘟,當前的goroutine會掛接到所有關(guān)聯(lián)的channel內(nèi)部的協(xié)程隊列上。 所以說單個goroutine是可以同時掛接到多個channel上的诽里,甚至可以同時掛接到同一個channel的發(fā)送協(xié)程隊列和接收協(xié)程隊列上搞疗。當一個阻塞的goroutine拿到了數(shù)據(jù)接觸阻塞的時候,它會從所有相關(guān)的channel隊列中移除掉。

channel簡單規(guī)則表

下標的活躍Channel表示即非空又非關(guān)閉的Channel

channel規(guī)則詳細解釋

空channel

關(guān)閉一個空channel會導致當前goroutine引發(fā)panic

向一個空channel發(fā)送值會導致當前的goroutine阻塞

從一個空channel接收值也會導致當前的goroutine阻塞

在空channel上的調(diào)用len和cap函數(shù)都統(tǒng)一返回零匿乃。

已關(guān)閉的Channel

關(guān)閉一個已關(guān)閉的channel會引發(fā)panic

向一個已關(guān)閉的channel發(fā)送值會引發(fā)panic桩皿。當這種send操作處于select塊里面的case語句上時,它會隨時導致select語句引發(fā)panic幢炸。

從一個已關(guān)閉的channel上接收值既不會阻塞也不能panic泄隔,它一直能成功返回。只是返回的第二個值ok永遠是false宛徊,表示接收到的v是在channel關(guān)閉之后拿到的佛嬉,對應得值也是相應元素類型的零值≌⑻欤可以無限循環(huán)從已關(guān)閉的channel上接收值暖呕。

活躍的Channel

關(guān)閉操作

從channel的接收協(xié)程隊列中移除所有的goroutine,并喚醒它們苞氮。

從channel的接收協(xié)程隊列中移除所有的goroutine湾揽,并喚醒它們。

一個已關(guān)閉的channel內(nèi)部的緩沖數(shù)組可能不是空的笼吟,沒有接收的這些值會導致channel對象永遠不會被垃圾回收库物。

發(fā)送操作

如果是阻塞型channel,那就從channel的接收協(xié)程隊列中移出第一個協(xié)程贷帮,然后把發(fā)送的值直接遞給這個協(xié)程戚揭。

如果是阻塞型channel,并且channel的接收協(xié)程隊列是空的撵枢,那么當前的協(xié)程將會阻塞民晒,并進入到channel的發(fā)送協(xié)程隊列里。

如果是緩沖型channel锄禽,并且緩沖數(shù)組里還有空間潜必,那么將發(fā)送的值添加到數(shù)組最后,當前協(xié)程不阻塞沟绪。

如果是緩沖型channel刮便,并且緩沖數(shù)組已經(jīng)滿了空猜,那么當前的協(xié)程將會阻塞绽慈,并進入到channel的發(fā)送協(xié)程隊列中。

接收操作

如果是緩沖型channel辈毯,并且緩沖數(shù)組有值坝疼,那么當前的協(xié)程不會阻塞,直接從數(shù)組中拿出第一個值谆沃。如果發(fā)送隊列非空钝凶,還需要將隊列中的第一個goroutine喚醒。

如果是阻塞型channel唁影,并且發(fā)送隊列非空的話耕陷,那么喚醒發(fā)送隊列第一個協(xié)程掂名,該協(xié)程會將發(fā)送的值直接遞給接收的協(xié)程。

如果是緩沖型channel哟沫,并且緩沖數(shù)組為空饺蔑,或者是阻塞型channel,并且發(fā)送協(xié)程隊列為空嗜诀,那么當前協(xié)程將會阻塞猾警,并加入到channel的接收協(xié)程隊列中。

總結(jié)

根據(jù)以上規(guī)則隆敢,我們可以得出以下結(jié)論

如果channel關(guān)閉了发皿,那么它的接收和發(fā)送協(xié)程隊列必然空了,但是它的緩沖數(shù)組可能還沒有空拂蝎。

channel的接收協(xié)程隊列和緩沖數(shù)組穴墅,同一個時間必然有一個是空的

channel的緩沖數(shù)組如果未滿,那么它的發(fā)送協(xié)程隊列必然是空的

對于緩沖型channel匣屡,同一時間它的接收和發(fā)送協(xié)程隊列封救,必然有一個是空的

對于非緩沖型channel,一般來說同一時間它的接收和發(fā)送協(xié)程隊列捣作,也必然有一個是空的誉结,但是有一個例外,那就是當它的發(fā)送操作和接收操作在同一個select塊里出現(xiàn)的時候券躁,兩個隊列都不是空的惩坑。

閱讀更多相關(guān)文章,關(guān)注知乎專欄【碼洞】

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末也拜,一起剝皮案震驚了整個濱河市以舒,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌慢哈,老刑警劉巖蔓钟,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異卵贱,居然都是意外死亡滥沫,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門键俱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來兰绣,“玉大人,你說我怎么就攤上這事编振∽罕纾” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長臀玄。 經(jīng)常有香客問我瓢阴,道長,這世上最難降的妖魔是什么健无? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任炫掐,我火速辦了婚禮,結(jié)果婚禮上睬涧,老公的妹妹穿的比我還像新娘募胃。我一直安慰自己,他們只是感情好畦浓,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布痹束。 她就那樣靜靜地躺著,像睡著了一般讶请。 火紅的嫁衣襯著肌膚如雪祷嘶。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天夺溢,我揣著相機與錄音论巍,去河邊找鬼。 笑死风响,一個胖子當著我的面吹牛嘉汰,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播状勤,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼鞋怀,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了持搜?” 一聲冷哼從身側(cè)響起密似,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎葫盼,沒想到半個月后残腌,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡贫导,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年抛猫,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片脱盲。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡邑滨,死狀恐怖日缨,靈堂內(nèi)的尸體忽然破棺而出钱反,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布面哥,位于F島的核電站哎壳,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏尚卫。R本人自食惡果不足惜归榕,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望吱涉。 院中可真熱鬧刹泄,春花似錦、人聲如沸怎爵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鳖链。三九已至姆蘸,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間芙委,已是汗流浹背逞敷。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留灌侣,地道東北人推捐。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像侧啼,于是被迫代替她去往敵國和親玖姑。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

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