channel是大家在Go中用的最頻繁的特性,也是Go最自豪的特性之一蛉加,你有沒(méi)有思考過(guò):
- Why:為什么要設(shè)計(jì)channel曹步?
- What:channel是什么樣的径荔?
- How:channel是如何實(shí)現(xiàn)的?
這篇文章颠黎,就來(lái)回答這3個(gè)問(wèn)題另锋。
channel解決什么問(wèn)題?
在Golang誕生之前狭归,各編程語(yǔ)言都使用多線程進(jìn)行編程夭坪,但多線程復(fù)雜、混亂过椎、難以管理室梅,對(duì)開(kāi)發(fā)者并不是多么友好。
Golang是Google為了解決高并發(fā)搜索而設(shè)計(jì)的潭流,它們想使用簡(jiǎn)單的方式竞惋,高效解決并發(fā)問(wèn)題,最后做成了灰嫉,然后又把Golang開(kāi)源了出來(lái)拆宛,以及到處推廣,所以Golang自從誕生之初讼撒,就風(fēng)風(fēng)火火浑厚。
從Golang文檔中,我們可以知道根盒,為啥Golang設(shè)計(jì)了channel钳幅,以及channel解決了什么問(wèn)題?
Concurrency is the key to designing high performance network services. Go's concurrency primitives (goroutines and channels) provide a simple and efficient means of expressing concurrent execution. In this talk we see how tricky concurrency problems can be solved gracefully with simple Go code.
Golang使用goroutine
和channel
簡(jiǎn)單炎滞、高效的解決并發(fā)問(wèn)題敢艰,channel解決的是goroutine之間的通信。
channel是怎么設(shè)計(jì)的册赛?
我們以為channel是一個(gè)通道:
實(shí)際上钠导,channel的內(nèi)在是這樣的:
channel設(shè)計(jì)涉及的數(shù)據(jù)結(jié)構(gòu)很簡(jiǎn)單:
- 基于數(shù)組的循環(huán)隊(duì)列,有緩沖的channel用它暫存數(shù)據(jù)
- 基于鏈表的單向隊(duì)列森瘪,用于保存阻塞在此channel上的goroutine
我本來(lái)想自己碼一篇channel的設(shè)計(jì)文章牡属,但已經(jīng)有大牛:Kavya深入分析了Channel的設(shè)計(jì),我也相信自己寫的肯定不如他好扼睬,所以我把Kavya在Gopher Con上的PPT推薦給你逮栅,如果你希望成為Go大牛,你一定要讀一下,現(xiàn)在請(qǐng)收藏好措伐。
Kavya在Gopher Con上的演講主題是:理解channel特纤,他并不是教你如何使用channel,而是把channel的設(shè)計(jì)和goroutine的調(diào)度結(jié)合起來(lái)废士,從內(nèi)在方式向你介紹叫潦。這份PPT足足有80頁(yè),包含了大量的動(dòng)畫官硝,非常容易理解矗蕊,你會(huì)了解到:
- channel的創(chuàng)建
- 各種場(chǎng)景的發(fā)送和接收
- goroutine的調(diào)度
- goroutine的阻塞和喚醒
- channel和goroutine在select操作下
Kavya的PPT應(yīng)該包含了channel的80%的設(shè)計(jì)思想,但也有一些缺失氢架,需要你閱讀源碼:
- channel關(guān)閉時(shí)傻咖,gorontine的處理
- 創(chuàng)建channel時(shí),不同的創(chuàng)建方法
- 讀channel時(shí)的非阻塞操作
- ...
PPT在此:Understanding Channels岖研,如果你有心卿操,還可以在這個(gè)網(wǎng)站看到Kavya關(guān)于goroutine調(diào)度的PPT,福利哦??孙援。(訪問(wèn)不了請(qǐng)翻墻害淤,或閱讀原文從博客文章最下面看Github備份)
channel是怎么實(shí)現(xiàn)的?
chan.go是channel的主要實(shí)現(xiàn)文件拓售,只有700行窥摄,十分佩服Go團(tuán)隊(duì),實(shí)現(xiàn)的如此精簡(jiǎn)础淤,卻發(fā)揮如此大的作用U阜拧!鸽凶!
看完Kavya的PPT币砂,你已經(jīng)可以直接看channel的源碼了,如果有任何問(wèn)題玻侥,思考一下你也可以想通决摧,如果有任何問(wèn)題可博客文章留言或公眾號(hào)私信進(jìn)行討論。
另外凑兰,推薦一篇在Medium(國(guó)外高質(zhì)量文章社區(qū))上獲得500+贊的源碼分析文章掌桩,非常詳細(xì)。
文章鏈接:Diving deep into the golang channels
我學(xué)到了什么票摇?
閱讀channel源碼我學(xué)到了一些東西拘鞋,分享給大家砚蓬。
channel的4個(gè)特性的實(shí)現(xiàn):
- channel的goroutine安全矢门,是通過(guò)mutex實(shí)現(xiàn)的。
- channel的FIFO,是通過(guò)循環(huán)隊(duì)列實(shí)現(xiàn)的祟剔。
- channel的通信:在goroutine間傳遞數(shù)據(jù)隔躲,是通過(guò)僅共享hchan+數(shù)據(jù)拷貝實(shí)現(xiàn)的。
- channel的阻塞是通過(guò)goroutine自己掛起物延,喚醒goroutine是通過(guò)對(duì)方goroutine喚醒實(shí)現(xiàn)的宣旱。
channel的其他實(shí)現(xiàn):
- 發(fā)送goroutine是可以訪問(wèn)接收goroutine的內(nèi)存空間的,接收goroutine也是可以直接訪問(wèn)發(fā)送goroutine的內(nèi)存空間的叛薯,看
sendDirect
浑吟、recvDirect
函數(shù)。 - 無(wú)緩沖的channel始終都是直接訪問(wèn)對(duì)方goroutine內(nèi)存的方式耗溜,把手伸到別人的內(nèi)存,把數(shù)據(jù)放到接收變量的內(nèi)存,或者從發(fā)送goroutine的內(nèi)存拷貝到自己內(nèi)存术陶。省掉了對(duì)方再加鎖獲取數(shù)據(jù)的過(guò)程缘缚。
- 接收goroutine讀不到數(shù)據(jù)和發(fā)送goroutine無(wú)法寫入數(shù)據(jù)時(shí),是把自己掛起的阿宅,這就是channel的阻塞操作候衍。阻塞的接收goroutine是由發(fā)送goroutine喚醒的,阻塞的發(fā)送goroutine是由接收goroutine喚醒的洒放,看
gopark
蛉鹿、goready
函數(shù)在chan.go
中的調(diào)用。 - 接收goroutine當(dāng)channel關(guān)閉時(shí)拉馋,讀channel會(huì)得到0值榨为,并不是channel保存了0值,而是它發(fā)現(xiàn)channel關(guān)閉了煌茴,把接收數(shù)據(jù)的變量的值設(shè)置為0值随闺。
- channel的操作/調(diào)用,是通過(guò)reflect實(shí)現(xiàn)的蔓腐,可以看reflect包的
makechan
,chansend
,chanrecv
函數(shù)矩乐。
如果閱讀chan_test.go還會(huì)學(xué)到一些騷操作,比如:
if <-stopCh {
// do stop
}
而不是寫成:
if stop := <-stopCh; stop {
// do stop
}
這就是關(guān)于channel的設(shè)計(jì)和實(shí)現(xiàn)的分享回论,希望你通過(guò)Kavya的PPT和代碼閱讀能深入了解channel散罕。
鏈接
- chan.go:https://github.com/golang/go/blob/master/src/runtime/chan.go
- chan_test.go:https://github.com/golang/go/blob/master/src/runtime/chan_test.go
- Understanding channels在Github的備份: https://github.com/Shitaibin/shitaibin.github.io/blob/hexo_resource/files/GopherCon_v10.0.pdf
- 如果這篇文章對(duì)你有幫助,請(qǐng)點(diǎn)個(gè)贊/喜歡傀蓉,感謝欧漱。
- 本文作者:大彬
- 如果喜歡本文,隨意轉(zhuǎn)載葬燎,但請(qǐng)保留此原文鏈接:http://www.lessisbetter.site/2019/03/03/golang-channel-design-and-source/