golang中的channel

channel通道

golang的并發(fā)模型是序列通信處理CSP(communicating sequential process)——使用通信來共享內(nèi)存 鸠真,避免goroutine因競爭共享內(nèi)存頻繁加鎖產(chǎn)生的性能問題屑咳。

基本數(shù)據(jù)結(jié)構(gòu)

golang中提供了一個特殊的類型channel實現(xiàn)goroutine之間的通信耗跛。channel類似于隊列杖刷,先進先出竹勉。channel數(shù)據(jù)結(jié)構(gòu)源碼在src/runtime/chan.go下。

chan 使用 hchan 表示肿嘲,它的傳參與賦值始終都是指針形式融击,每個 hchan 對象代表著一個 chan。

  • hchan 中包含一個緩沖區(qū) buf睦刃,它表示已經(jīng)發(fā)送但是還未被接收的數(shù)據(jù)緩存砚嘴。buf 的大小由創(chuàng)建 chan 時的參數(shù)來決定。qcount 表示當(dāng)前緩沖區(qū)中有效數(shù)據(jù)的總量涩拙,dataqsiz 表示緩沖區(qū)的大小际长,對于無緩沖區(qū)通道而言 dataqsiz 的值為 0。如果 qcount 和 dataqsiz 的值相同兴泥,則表示緩沖區(qū)用完了工育。
  • buf ** 緩沖區(qū)表示的是一個環(huán)形隊列** 。其中 **sendx ** 表示下一個發(fā)送的地址搓彻, **recvx ** 表示下一個接收的地址如绸。
  • elemtype 是channel的中存放的具體類型嘱朽,每一個通道都是一個具體類型的導(dǎo)管,也就是聲明channel的時候需要為其指定元素類型怔接。
  • recvq 表示等待接收的 sudog (goroutine)列表搪泳,一個接收語句執(zhí)行時,如果緩沖區(qū)沒有數(shù)據(jù)而且當(dāng)前沒有別的發(fā)送者在等待扼脐,那么執(zhí)行者 goroutine 會被掛起岸军,并且將對應(yīng)的 sudog 對象放到 recvq 中。
  • sendq 類似于 recvq瓦侮,一個發(fā)送語句執(zhí)行時艰赞,如果緩沖區(qū)已經(jīng)滿了,而且沒有接收者在等待肚吏,那么執(zhí)行者 goroutine 會被掛起方妖,并且將對應(yīng)的 sudog 放到 sendq 中。
  • closed 表示通道是否已經(jīng)被關(guān)閉罚攀,0 代表沒有被關(guān)閉党觅,非 0 值代表已經(jīng)被關(guān)閉。
  • lock 用于對 hchan 加鎖
type hchan struct {
    qcount   uint           // total data in the queue
    dataqsiz uint           // size of the circular queue
    buf      unsafe.Pointer // points to an array of dataqsiz elements
    elemsize uint16
    closed   uint32
    elemtype *_type // element type
    sendx    uint   // send index
    recvx    uint   // receive index
    recvq    waitq  // list of recv waiters
    sendq    waitq  // list of send waiters

    // lock protects all fields in hchan, as well as several
    // fields in sudogs blocked on this channel.
    //
    // Do not change another G's status while holding this lock
    // (in particular, do not ready a G), as this can deadlock
    // with stack shrinking.
    lock mutex
}

創(chuàng)建一個channel

var name chan elemType //聲明中需要指定channel的具體類型 eg. var c chan int 聲明了一個int的channel

我們使用make()方法創(chuàng)建斋泄,可以指定chan的緩存區(qū)大小仔役。

c1 := make(chan int, 10) //make創(chuàng)建一個int類型的channel,容量大小為10
c2 := make(chan int) // 無緩存的channel

make()函數(shù)最后會調(diào)用底層的makechan()函數(shù)是己,返回一個通道的指針。當(dāng)我們復(fù)制一個channel或用于函數(shù)參數(shù)傳遞時任柜,我們只是拷貝了一個channel引用卒废,因此調(diào)用者和被調(diào)用者將引用同一個channel對象。和其它的引用類型一樣宙地,channel的零值也是nil摔认。

兩個相同類型的channel可以使用==運算符比較。如果兩個channel引用的是相同的對象宅粥,那么比較的結(jié)果為真参袱。一個channel也可以和nil進行比較。

通道的操作

發(fā)送(直譯秽梅,其實感覺翻譯成存入更好)

ch := make(chan int,0) // 創(chuàng)建一個無緩存區(qū)的int channel
ch <- 998              // 向通道發(fā)送一個int類型的值10 

接收(取出)

receiver := <-ch    //將通道ch的值取出抹蚀,賦值給變量receiver
<- ch               //將通道ch的值取出,忽略結(jié)果

關(guān)閉

close(ch)   // 關(guān)閉通道

關(guān)于關(guān)閉通道需要注意的事情是企垦,只有在通知接收方goroutine所有的數(shù)據(jù)都發(fā)送完畢的時候才需要關(guān)閉通道环壤。通道是可以被垃圾回收機制回收的,它和關(guān)閉文件是不一樣的钞诡,在結(jié)束操作之后關(guān)閉文件是必須要做的郑现,但關(guān)閉通道不是必須的湃崩。

關(guān)閉后的通道有以下特點:

  1. 對一個關(guān)閉的通道再發(fā)送值就會導(dǎo)致panic。
  2. 對一個關(guān)閉的通道進行接收會一直獲取值直到通道為空接箫。
  3. 對一個關(guān)閉的并且沒有值的通道執(zhí)行接收操作會得到對應(yīng)類型的零值攒读。
  4. 關(guān)閉一個已經(jīng)關(guān)閉的通道會導(dǎo)致panic。

channel的種類

不帶緩存的channel

一個基于無緩存Channels的發(fā)送操作將導(dǎo)致發(fā)送者goroutine阻塞辛友,直到另一個goroutine在相同的Channels上執(zhí)行接收操作薄扁,當(dāng)發(fā)送的值通過Channels成功傳輸之后,兩個goroutine可以繼續(xù)執(zhí)行后面的語句瞎领。反之泌辫,如果接收操作先發(fā)生,那么接收者goroutine也將阻塞九默,直到有另一個goroutine在相同的Channels上執(zhí)行發(fā)送操作震放。

基于無緩存Channels的發(fā)送和接收操作將導(dǎo)致兩個goroutine做一次同步操作。 因為這個原因驼修,無緩存Channels有時候也被稱為同步Channels 殿遂。當(dāng)通過一個無緩存Channels發(fā)送數(shù)據(jù)時,接收者收到數(shù)據(jù)發(fā)生在再次喚醒發(fā)送者goroutine之前乙各。

單向channel

當(dāng)一個channel作為一個函數(shù)參數(shù)時墨礁,它一般總是被專門用于只發(fā)送或者只接收。為了表明這種意圖并防止被濫用耳峦,Go語言的類型系統(tǒng)提供了單方向的channel類型恩静,分別用于只發(fā)送或只接收的channel。類型 chan<- int 表示一個只發(fā)送int的channel蹲坷,只能發(fā)送不能接收驶乾。相反运提,類型<-chan int 表示一個只接收int的channel彤恶,只能接收不能發(fā)送护昧。(箭頭<-和關(guān)鍵字chan的相對位置表明了channel的方向 侮腹。)這種限制將在編譯期檢測嬉橙。

func f1(out chan<- int) {}
func f2(out chan<- int, in <-chan int){}

在調(diào)用f1或f2的時候挪圾,傳入的chan類型參數(shù)會自動隱式轉(zhuǎn)換為chan<- int<-chan int類型丛版,這種轉(zhuǎn)換只是單向的产捞,沒有單向channel類型轉(zhuǎn)換為chan類型乞旦。

因為關(guān)閉操作只用于斷言不再向channel發(fā)送新的數(shù)據(jù)贼穆,所以只有在發(fā)送者所在的goroutine才會調(diào)用close函數(shù),因此對一個只接收的channel調(diào)用close將是一個編譯錯誤杆查。

帶緩存的channel

帶緩存的Channel內(nèi)部持有一個元素隊列扮惦。隊列的最大容量是在調(diào)用make函數(shù)創(chuàng)建channel時通過第二個參數(shù)指定的。通過內(nèi)置函數(shù)cap() 可以獲得channel的容量亲桦。對于內(nèi)置的len函數(shù)崖蜜,如果傳入的是channel浊仆,那么將返回channel內(nèi)部緩存隊列中有效元素的個數(shù)。因為在并發(fā)程序中該信息會隨著接收操作而失效豫领,但是它對某些故障診斷和性能優(yōu)化會有幫助抡柿。

package main

import "fmt"

func main() {
    ch := make(chan int, 10) // 創(chuàng)建一個最大容量為10的channel
    ch <- 233
    ch <- 2
    ch <- 3
    fmt.Println(cap(ch)) // 10
    fmt.Println(len(ch)) // 3
    fmt.Println(<-ch)    // 233
    fmt.Println(len(ch)) // 2
}

向緩存Channel的發(fā)送操作就是向內(nèi)部緩存隊列的尾部插入元素,接收操作則是從隊列的頭部刪除元素等恐。如果內(nèi)部緩存隊列是滿的洲劣,那么發(fā)送操作將阻塞直到因另一個goroutine執(zhí)行接收操作而釋放了新的隊列空間。相反课蔬,如果channel是空的囱稽,接收操作將阻塞直到有另一個goroutine執(zhí)行發(fā)送操作而向隊列插入元素。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末二跋,一起剝皮案震驚了整個濱河市战惊,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌扎即,老刑警劉巖吞获,帶你破解...
    沈念sama閱讀 210,914評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異谚鄙,居然都是意外死亡各拷,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評論 2 383
  • 文/潘曉璐 我一進店門闷营,熙熙樓的掌柜王于貴愁眉苦臉地迎上來烤黍,“玉大人,你說我怎么就攤上這事傻盟∥萌伲” “怎么了?”我有些...
    開封第一講書人閱讀 156,531評論 0 345
  • 文/不壞的土叔 我叫張陵莫杈,是天一觀的道長。 經(jīng)常有香客問我奢入,道長筝闹,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,309評論 1 282
  • 正文 為了忘掉前任腥光,我火速辦了婚禮关顷,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘武福。我一直安慰自己议双,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,381評論 5 384
  • 文/花漫 我一把揭開白布捉片。 她就那樣靜靜地躺著平痰,像睡著了一般汞舱。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上宗雇,一...
    開封第一講書人閱讀 49,730評論 1 289
  • 那天昂芜,我揣著相機與錄音,去河邊找鬼赔蒲。 笑死泌神,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的舞虱。 我是一名探鬼主播欢际,決...
    沈念sama閱讀 38,882評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼矾兜!你這毒婦竟也來了损趋?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,643評論 0 266
  • 序言:老撾萬榮一對情侶失蹤焕刮,失蹤者是張志新(化名)和其女友劉穎舶沿,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體配并,經(jīng)...
    沈念sama閱讀 44,095評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡括荡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,448評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了溉旋。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片畸冲。...
    茶點故事閱讀 38,566評論 1 339
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖观腊,靈堂內(nèi)的尸體忽然破棺而出邑闲,到底是詐尸還是另有隱情,我是刑警寧澤梧油,帶...
    沈念sama閱讀 34,253評論 4 328
  • 正文 年R本政府宣布苫耸,位于F島的核電站,受9級特大地震影響儡陨,放射性物質(zhì)發(fā)生泄漏褪子。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,829評論 3 312
  • 文/蒙蒙 一骗村、第九天 我趴在偏房一處隱蔽的房頂上張望嫌褪。 院中可真熱鬧,春花似錦胚股、人聲如沸笼痛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽缨伊。三九已至摘刑,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間倘核,已是汗流浹背泣侮。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留紧唱,地道東北人活尊。 一個月前我還...
    沈念sama閱讀 46,248評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像漏益,于是被迫代替她去往敵國和親蛹锰。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,440評論 2 348