這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
golang 的 select 的功能和 select, poll, epoll 相似,就是監聽 IO 操作,當 IO 操作發生時,觸發相應的動作。
樣本:
ch1 := make (chan int, 1)ch2 := make (chan int, 1)...select {case <-ch1: fmt.Println("ch1 pop one element")case <-ch2: fmt.Println("ch2 pop one element")}
注意到 select 的代碼形式和 switch 非常相似,不過 select 的 case 裡的動作陳述式只能是【IO 操作】 。
此樣本裡面 select 會一直等待等到某個 case 陳述式完成,也就是等到成功從 ch1 或者 ch2 中讀到資料。則 select 語句結束。
【使用 select 實現 timeout 機制】
如下:
timeout := make (chan bool, 1)go func() { time.Sleep(1e9) // sleep one second timeout <- true}()ch := make (chan int)select {case <- ch:case <- timeout: fmt.Println("timeout!")}
當逾時時間到的時候,case2 會操作成功。所以 select 語句則會退出。而不是一直阻塞在 ch 的讀取操作上。從而實現了對 ch 讀取操作的逾時設定。
下面這個更有意思一點。
當 select 語句帶有 default 的時候:
ch1 := make (chan int, 1)ch2 := make (chan int, 1)select {case <-ch1: fmt.Println("ch1 pop one element")case <-ch2: fmt.Println("ch2 pop one element")default: fmt.Println("default")}
此時因為 ch1 和 ch2 都為空白,所以 case1 和 case2 都不會讀取成功。則 select 執行 default 語句。
就是因為這個 default 特性,我們可以使用 select 語句來檢測 chan 是否已經滿了。
如下:
ch := make (chan int, 1)ch <- 1select {case ch <- 2:default: fmt.Println("channel is full !")}
因為 ch 插入 1 的時候已經滿了,當 ch 要插入 2 的時候,發現 ch 已經滿了(case1 阻塞住),則 select 執行 default 語句。這樣就可以實現對 channel 是否已滿的檢測,而不是一直等待。
比如我們有一個服務,當請求進來的時候我們會產生一個 job 扔進 channel,由其他協程從 channel 中擷取 job 去執行。但是我們希望當 channel 瞞了的時候,將該 job 拋棄並回複 【服務繁忙,請稍微再試。】就可以用 select 實現該需求。
關於記憶體回收
c++ 寫久了的人,剛接觸 golang 的時候最不能理解的就是為什麼作者要支援記憶體回收。不管是從記憶體回收行程的實現上看,還是對於程式員編程習慣的養成方面,都避免不了編寫出的程式效能損失。但是寫了幾天 golang 之後,又覺得有記憶體回收確實大大減輕程式員的心智負擔,降低編程門檻,提高編程效率。讓我聯想到 彙編 和 C語言 的關係,即使 C語言的效能不如彙編寫出來的高,但是後者還是顛覆了前者。
參考
順便說說,我自己對這兩本書的評價:
【go語言程式設計】和【go語言編程】這兩本書我都看過。 前者偏向於學院派教科書,語言表述非常囉嗦。 後者偏向於工程派,我在看完書之後寫代碼的時候,有疑問了會經常翻看【go 語言編程】尋找對應的內容看,作者表述言簡意賅,一翻我就能找到我想要的答案,讓人感覺是自己想問的,作者早就思考過並已經很好的介紹和解釋。 說白了,如果你是編程初學者,那麼請看【go語言程式設計】。如果是有良好基礎的開發人員,直接看【go語言編程】會高效很多。