Golang Channel用法簡(jiǎn)編九 29 bigwhite技術(shù)志 Actor, BestPractice, Blog, Blogger, Channel, Concurrent, container, CSP, docker, github, Go, Golang, Google, GopherCon, LXC, Opensource, Programmer, TIOBE, 內(nèi)存模型, 博客, 容器, 工作, 布道師, 并發(fā), 開(kāi)源, 思考, 感悟, 程序員, 編程 暫無(wú)評(píng)論 在進(jìn)入正式內(nèi)容前,我這里先順便轉(zhuǎn)發(fā)一則消息,那就是Golang 1.3.2已經(jīng)正式發(fā)布了。國(guó)內(nèi)的golangtc已經(jīng)鏡像了的安裝包下載頁(yè)面,國(guó)內(nèi)go程序員與愛(ài)好者們可以到"Golang中 國(guó)",即去下載go 1.3.2版本。 Go這門語(yǔ)言也許你還不甚了解,甚至是完全不知道,這也有情可原,畢竟Go在TIOBE編程語(yǔ)言排行榜上位列30開(kāi)外。但近期使用Golang 實(shí)現(xiàn)的一殺手級(jí)應(yīng)用 Docker你卻不該不知道。docker目前火得是一塌糊涂啊。你去國(guó)內(nèi)外各大技術(shù)站點(diǎn)用眼輕瞥一下,如 果沒(méi)有涉及到“docker”字樣新聞的站點(diǎn)建 議你以后就不要再去訪問(wèn)了^_^。Docker是啥、怎么用以及基礎(chǔ)實(shí)踐可以參加國(guó)內(nèi)一位仁兄的經(jīng)驗(yàn)之作:《 Docker – 從入門到實(shí)踐》。 據(jù)我了解,目前國(guó)內(nèi)試水Go語(yǔ)言開(kāi)發(fā)后臺(tái)系統(tǒng)的大公司與初創(chuàng)公司日益增多,比如七牛、京東、小米,盛大,金山,東軟,搜狗等,在這里我們可以看到一些公司的Go語(yǔ)言應(yīng)用列表,并且目前這個(gè)列表似乎依舊在豐富中。國(guó)內(nèi)Go語(yǔ)言的推廣與布道也再穩(wěn)步推進(jìn)中,不過(guò)目前來(lái)看多以Go入 門與基礎(chǔ)為主題,Go idioms、tips或Best Practice的Share并不多見(jiàn),想必國(guó)內(nèi)的先行者、布道師們還在韜光養(yǎng)晦,積攢經(jīng)驗(yàn),等到時(shí)機(jī)來(lái)臨再厚積薄發(fā)。另外國(guó)內(nèi)似乎還沒(méi)有一個(gè)針對(duì)Go的 布道平臺(tái),比如Golang技術(shù)大會(huì)之類的的平臺(tái)。 在國(guó)外,雖然Go也剛剛起步,但在Golang share的廣度和深度方面顯然更進(jìn)一步。Go的國(guó)際會(huì)議目前還不多,除了Golang老東家Google在自己的各種大會(huì)上留給Golang展示自己的 機(jī)會(huì)外,由 Gopher Academy 發(fā)起的GopherCon 會(huì)議也于今年第一次舉行,并放出諸多高質(zhì)量資料,在這里可以下載。歐洲的Go語(yǔ)言大會(huì).dotgo也即將開(kāi)幕,估計(jì)后續(xù)這兩個(gè)大會(huì)將撐起Golang技術(shù)分享 的旗幟。 言歸正傳,這里要寫(xiě)的東西并非原創(chuàng),自己的Go僅僅算是入門級(jí)別,工程經(jīng)驗(yàn)、Best Practice等還談不上有多少,因此這里主要是針對(duì)GopherCon2014上的“舶來(lái)品”的學(xué)習(xí)心得。來(lái)自CloudFlare的工程師John Graham-Cumming談了關(guān)于 Channel的實(shí)踐經(jīng)驗(yàn),這里針對(duì)其分享的內(nèi)容,記錄一些學(xué)習(xí)體會(huì)和理解,并結(jié)合一些外延知識(shí),也可以算是一種學(xué)習(xí)筆記吧,僅供參考。 一、Golang并發(fā)基礎(chǔ)理論 Golang在并發(fā)設(shè)計(jì)方面參考了C.A.R Hoare的CSP,即Communicating Sequential Processes并發(fā)模型理論。但就像John Graham-Cumming所說(shuō)的那樣,多數(shù)Golang程序員或愛(ài)好者僅僅停留在“知道”這一層次,理解CSP理論的并不多,畢竟多數(shù)程序員是搞工程 的。不過(guò)要想系統(tǒng)學(xué)習(xí)CSP的人可以從這里下載到CSP論文的最新版本。 維基百科中概要羅列了CSP模型與另外一種并發(fā)模型Actor模型的區(qū)別: Actor模型廣義上講與CSP模型很相似。但兩種模型就提供的原語(yǔ)而言,又有一些根本上的不同之處: 二、Go Channel基本操作語(yǔ)法 Go Channel的基本操作語(yǔ)法如下: c := make(chan bool) //創(chuàng)建一個(gè)無(wú)緩沖的bool型Channel? 不帶緩沖的Channel兼具通信和同步兩種特性,頗受青睞。 三、Channel用作信號(hào)(Signal)的場(chǎng)景 1、等待一個(gè)事件(Event) 等待一個(gè)事件,有時(shí)候通過(guò)close一個(gè)Channel就足夠了。例如: //testwaitevent1.go import "fmt" func main() { 這里main goroutine通過(guò)"<-c"來(lái)等待sub goroutine中的“完成事件”,sub goroutine通過(guò)close channel促發(fā)這一事件。當(dāng)然也可以通過(guò)向Channel寫(xiě)入一個(gè)bool值的方式來(lái)作為事件通知。main goroutine在channel c上沒(méi)有任何數(shù)據(jù)可讀的情況下會(huì)阻塞等待。 關(guān)于輸出結(jié)果: 根據(jù)《Go memory model》中關(guān)于close channel與recv from channel的order的定義:The closing of a channel happens before a receive that returns a zero value because the channel is closed. 我們可以很容易判斷出上面程序的輸出結(jié)果: Begin doing something! 如果將close(c)換成c<-true,則根據(jù)《Go memory model》中的定義:A receive from an unbuffered channel happens before the send on that channel completes. 2、協(xié)同多個(gè)Goroutines 同上,close channel還可以用于協(xié)同多個(gè)Goroutines,比如下面這個(gè)例子,我們創(chuàng)建了100個(gè)Worker Goroutine,這些Goroutine在被創(chuàng)建出來(lái)后都阻塞在"<-start"上,直到我們?cè)趍ain goroutine中給出開(kāi)工的信號(hào):"close(start)",這些goroutines才開(kāi)始真正的并發(fā)運(yùn)行起來(lái)。 //testwaitevent2.go import "fmt" func worker(start chan bool, index int) { func main() { 3、Select 【select的基本操作】 select { case y, ok := <- someOtherchan: case outputChan <- z: default: 【慣用法:for/select】 我們?cè)谑褂胹elect時(shí)很少只是對(duì)其進(jìn)行一次evaluation,我們常常將其與for {}結(jié)合在一起使用,并選擇適當(dāng)時(shí)機(jī)從for{}中退出。 for { case y, ok := <- someOtherchan: case outputChan <- z: default: 【終結(jié)workers】 下面是一個(gè)常見(jiàn)的終結(jié)sub worker goroutines的方法,每個(gè)worker goroutine通過(guò)select監(jiān)視一個(gè)die channel來(lái)及時(shí)獲取main goroutine的退出通知。 //testterminateworker1.go import ( func worker(die chan bool, index int) { func main() { for i := 1; i <= 100; i++ { time.Sleep(time.Second * 5) 【終結(jié)驗(yàn)證】 有時(shí)候終結(jié)一個(gè)worker后,main goroutine想確認(rèn)worker routine是否真正退出了,可采用下面這種方法: //testterminateworker2.go import ( func worker(die chan bool) { func main() { go worker(die) die <- true 【關(guān)閉的Channel永遠(yuǎn)不會(huì)阻塞】 下面演示在一個(gè)已經(jīng)關(guān)閉了的channel上讀寫(xiě)的結(jié)果: //testoperateonclosedchannel.go import "fmt" func main() { x, ok := <-cb ci := make(chan int) cb <- true $go run testoperateonclosedchannel.go 可以看到在一個(gè)已經(jīng)close的unbuffered channel上執(zhí)行讀操作,回返回channel對(duì)應(yīng)類型的零值,比如bool型channel返回false,int型channel返回0。但向close的channel寫(xiě)則會(huì)觸發(fā)panic。不過(guò)無(wú)論讀寫(xiě)都不會(huì)導(dǎo)致阻塞。 【關(guān)閉帶緩存的channel】 將unbuffered channel換成buffered channel會(huì)怎樣?我們看下面例子: //testclosedbufferedchannel.go import "fmt" func main() { c <- 1 $go run testclosedbufferedchannel.go 可以看出帶緩沖的channel略有不同。盡管已經(jīng)close了,但我們依舊可以從中讀出關(guān)閉前寫(xiě)入的3個(gè)值。第四次讀取時(shí),則會(huì)返回該channel類型的零值。向這類channel寫(xiě)入操作也會(huì)觸發(fā)panic。 【range】 Golang中的range常常和channel并肩作戰(zhàn),它被用來(lái)從channel中讀取所有值。下面是一個(gè)簡(jiǎn)單的實(shí)例: //testrange.go import "fmt" func generator(strings chan string) { func main() { 四、隱藏狀態(tài) 下面通過(guò)一個(gè)例子來(lái)演示一下channel如何用來(lái)隱藏狀態(tài): 1、例子:唯一的ID服務(wù) //testuniqueid.go import "fmt" func newUniqueIDService() <-chan string { $ go run testuniqueid.go newUniqueIDService通過(guò)一個(gè)channel與main goroutine關(guān)聯(lián),main goroutine無(wú)需知道uniqueid實(shí)現(xiàn)的細(xì)節(jié)以及當(dāng)前狀態(tài),只需通過(guò)channel獲得最新id即可。 五、默認(rèn)情況 我想這里John Graham-Cumming主要是想告訴我們select的default分支的實(shí)踐用法。 1、select for non-blocking receive idle:= make(chan []byte, 5) //用一個(gè)帶緩沖的channel構(gòu)造一個(gè)簡(jiǎn)單的隊(duì)列 select { 2、select for non-blocking send idle:= make(chan []byte, 5) //用一個(gè)帶緩沖的channel構(gòu)造一個(gè)簡(jiǎn)單的隊(duì)列 select { } 六、Nil Channels 1、nil channels阻塞 對(duì)一個(gè)沒(méi)有初始化的channel進(jìn)行讀寫(xiě)操作都將發(fā)生阻塞,例子如下: package main func main() { $go run testnilchannel.go package main func main() { $go run testnilchannel.go 2、nil channel在select中很有用 看下面這個(gè)例子: //testnilchannel_bad.go import "fmt" func main() { go func() { for { 我們?cè)酒谕绦蚪惶孑敵?和7兩個(gè)數(shù)字,但實(shí)際的輸出結(jié)果卻是: 5 再仔細(xì)分析代碼,原來(lái)select每次按case順序evaluate: 我們利用nil channel來(lái)改進(jìn)這個(gè)程序,以實(shí)現(xiàn)我們的意圖,代碼如下: //testnilchannel.go import "fmt" func main() { go func() { for { $go run testnilchannel.go 可以看出:通過(guò)將已經(jīng)關(guān)閉的channel置為nil,下次select將會(huì)阻塞在該channel上,使得select繼續(xù)下面的分支evaluation。 七、Timers 1、超時(shí)機(jī)制Timeout 帶超時(shí)機(jī)制的select是常規(guī)的tip,下面是示例代碼,實(shí)現(xiàn)30s的超時(shí)select: func worker(start chan bool) { 2、心跳HeartBeart 與timeout實(shí)現(xiàn)類似,下面是一個(gè)簡(jiǎn)單的心跳select實(shí)現(xiàn): func worker(start chan bool) { 2014, bigwhite. 版權(quán)所有. Related posts: |
|
來(lái)自: icecity1306 > 《可能用到系統(tǒng)》