Hi,大家好,我是明哥,
在自己學習 Golang 的這段時間里,我寫了詳細的學習筆記放在我的個人微信公眾號 《Go編程時光》,對于 Go 語言,我也算是個初學者,因此寫的東西應該會比較適合剛接觸的同學,如果你也是剛學習 Go 語言,不防關注一下,一起學習,一起成長,
我的在線博客:http://golang.iswbm.com
我的 Github:github.com/iswbm/GolangCodingTime
前面寫過兩節關于 switch-case 的文章,分別是:
流程控制:switch-case
Go 語言中的型別斷言
今天要學習一個跟 switch-case 很像,但還有點個人特色 的 select-case,這一節本應該放在 學習 Go 協程:詳解信道/通道 里一起講的,但是當時漏了,直到有讀者給我提出,才注意到,今天就用這篇文章補充一下,
跟 switch-case 相比,select-case 用法比較單一,它僅能用于 信道/通道 的相關操作,
select {
case 運算式1:
<code>
case 運算式2:
<code>
default:
<code>
}
接下來,我們來看幾個例子幫助理解這個 select 的模型,
1. 最簡單的例子
先創建兩個信道,并在 select 前往 c2 發送資料
package main
import (
"fmt"
)
func main() {
c1 := make(chan string, 1)
c2 := make(chan string, 1)
c2 <- "hello"
select {
case msg1 := <-c1:
fmt.Println("c1 received: ", msg1)
case msg2 := <-c2:
fmt.Println("c2 received: ", msg2)
default:
fmt.Println("No data received.")
}
}
在運行 select 時,會遍歷所有(如果有機會的話)的 case 運算式,只要有一個信道有接收到資料,那么 select 就結束,所以輸出如下
c2 received: hello
2. 避免造成死鎖
select 在執行程序中,必須命中其中的某一分支,
如果在遍歷完所有的 case 后,若沒有命中(命中:也許這樣描述不太準確,我本意是想說可以執行信道的操作陳述句)任何一個 case 運算式,就會進入 default 里的代碼分支,
但如果你沒有寫 default 分支,select 就會阻塞,直到有某個 case 可以命中,而如果一直沒有命中,select 就會拋出 deadlock 的錯誤,就像下面這樣子,
package main
import (
"fmt"
)
func main() {
c1 := make(chan string, 1)
c2 := make(chan string, 1)
// c2 <- "hello"
select {
case msg1 := <-c1:
fmt.Println("c1 received: ", msg1)
case msg2 := <-c2:
fmt.Println("c2 received: ", msg2)
// default:
// fmt.Println("No data received.")
}
}
運行后輸出如下
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [select]:
main.main()
/Users/MING/GolandProjects/golang-test/main.go:13 +0x10f
exit status 2
解決這個問題的方法有兩種
一個是,養成好習慣,在 select 的時候,也寫好 default 分支代碼,盡管你 default 下沒有寫任何代碼,
package main
import (
"fmt"
)
func main() {
c1 := make(chan string, 1)
c2 := make(chan string, 1)
// c2 <- "hello"
select {
case msg1 := <-c1:
fmt.Println("c1 received: ", msg1)
case msg2 := <-c2:
fmt.Println("c2 received: ", msg2)
default:
}
}
另一個是,讓其中某一個信道可以接收到資料
package main
import (
"fmt"
"time"
)
func main() {
c1 := make(chan string, 1)
c2 := make(chan string, 1)
// 開啟一個協程,可以發送資料到信道
go func() {
time.Sleep(time.Second * 1)
c2 <- "hello"
}()
select {
case msg1 := <-c1:
fmt.Println("c1 received: ", msg1)
case msg2 := <-c2:
fmt.Println("c2 received: ", msg2)
}
}
3. select 隨機性
之前學過 switch 的時候,知道了 switch 里的 case 是順序執行的,但在 select 里卻不是,
通過下面這個例子的執行結果就可以看出

4. select 的超時
當 case 里的信道始終沒有接收到資料時,而且也沒有 default 陳述句時,select 整體就會阻塞,但是有時我們并不希望 select 一直阻塞下去,這時候就可以手動設定一個超時時間,
package main
import (
"fmt"
"time"
)
func makeTimeout(ch chan bool, t int) {
time.Sleep(time.Second * time.Duration(t))
ch <- true
}
func main() {
c1 := make(chan string, 1)
c2 := make(chan string, 1)
timeout := make(chan bool, 1)
go makeTimeout(timeout, 2)
select {
case msg1 := <-c1:
fmt.Println("c1 received: ", msg1)
case msg2 := <-c2:
fmt.Println("c2 received: ", msg2)
case <-timeout:
fmt.Println("Timeout, exit.")
}
}
輸出如下
Timeout, exit.
5. 讀取/寫入都可以
上面例子里的 case,好像都只從信道中讀取資料,但實際上,select 里的 case 運算式只要求你是對信道的操作即可,不管你是往信道寫入資料,還是從信道讀出資料,
package main
import (
"fmt"
)
func main() {
c1 := make(chan int, 2)
c1 <- 2
select {
case c1 <- 4:
fmt.Println("c1 received: ", <-c1)
fmt.Println("c1 received: ", <-c1)
default:
fmt.Println("channel blocking")
}
}
輸出如下
c1 received: 2
c1 received: 4
6. 總結一下
select 與 switch 原理很相似,但它的使用場景更特殊,學習了本篇文章,你需要知道如下幾點區別:
- select 只能用于 channel 的操作(寫入/讀出),而 switch 則更通用一些;
- select 的 case 是隨機的,而 switch 里的 case 是順序執行;
- select 要注意避免出現死鎖,同時也可以自行實作超時機制;
- select 里沒有類似 switch 里的 fallthrough 的用法;
- select 不能像 switch 一樣接函式或其他運算式,
系列導讀
01. 開發環境的搭建(Goland & VS Code)
02. 學習五種變數創建的方法
03. 詳解資料型別:****整形與浮點型
04. 詳解資料型別:byte、rune與string
05. 詳解資料型別:陣列與切片
06. 詳解資料型別:字典與布爾型別
07. 詳解資料型別:指標
08. 面向物件編程:結構體與繼承
09. 一篇文章理解 Go 里的函式
10. Go語言流程控制:if-else 條件陳述句
11. Go語言流程控制:switch-case 選擇陳述句
12. Go語言流程控制:for 回圈陳述句
13. Go語言流程控制:goto 無條件跳轉
14. Go語言流程控制:defer 延遲呼叫
15. 面向物件編程:介面與多型
16. 關鍵字:make 和 new 的區別?
17. 一篇文章理解 Go 里的陳述句塊與作用域
18. 學習 Go 協程:goroutine
19. 學習 Go 協程:詳解信道/通道
20. 幾個信道死鎖經典錯誤案例詳解
21. 學習 Go 協程:WaitGroup
22. 學習 Go 協程:互斥鎖和讀寫鎖
23. Go 里的例外處理:panic 和 recover
24. 超詳細解讀 Go Modules 前世今生及入門使用
25. Go 語言中關于包匯入必學的 8 個知識點
26. 如何開源自己寫的模塊給別人用?
27. 說說 Go 語言中的型別斷言?
28. 這五點帶你理解Go語言的select用法

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/6568.html
標籤:Go
上一篇:Go檔案操作
下一篇:golang orm
