GO select用法详解
golang 的 select 就是監(jiān)聽 IO 操作,當(dāng) IO 操作發(fā)生時(shí),觸發(fā)相應(yīng)的動(dòng)作。?
在執(zhí)行select語(yǔ)句的時(shí)候,運(yùn)行時(shí)系統(tǒng)會(huì)自上而下地判斷每個(gè)case中的發(fā)送或接收操作是否可以被立即執(zhí)行(立即執(zhí)行:意思是當(dāng)前Goroutine不會(huì)因此操作而被阻塞)
select的用法與switch非常類似,由select開始一個(gè)新的選擇塊,每個(gè)選擇條件由case語(yǔ)句來描述。與switch語(yǔ)句可以選擇任何可使用相等比較的條件相比,select有比較多的限制,其中最大的一條限制就是每個(gè)case語(yǔ)句里必須是一個(gè)IO操作,確切的說,應(yīng)該是一個(gè)面向channel的IO操作。
下面這段話來自官方文檔:
A "select" statement chooses which of a set of possible?send?or?receive?operations will proceed. It looks similar to a?"switch"?statement but with the cases all referring to communication operations.
?
語(yǔ)法格式如下:
select {
case?SendStmt:
? //statements
case?RecvStmt:
? //statements
default:
? //statements
}
其中,
SendStmt : channelVariable <- value
RecvStmt : variable <-channelVariable
?
A case with a RecvStmt may assign the result of a RecvExpr to one or two variables, which may be declared using a?short variable declaration(IdentifierList := value). The RecvExpr must be a (possibly parenthesized) receive operation(<-channelVariable). There can be at most one default case and it may appear anywhere in the list of cases.
?
示例:
?
????ch1 := make(chan int, 1) ????ch2 := make(chan int, 1) ????ch1 <- 1 ????select { ????case e1 := <-ch1: ????????//如果ch1通道成功讀取數(shù)據(jù),則執(zhí)行該case處理語(yǔ)句 ????????fmt.Printf("1th case is selected. e1=%v", e1) ????case e2 := <-ch2: ????????//如果ch2通道成功讀取數(shù)據(jù),則執(zhí)行該case處理語(yǔ)句 ????????fmt.Printf("2th case is selected. e2=%v", e2) ????default: ????????//如果上面case都沒有成功,則進(jìn)入default處理流程 ????????fmt.Println("default!.") ????}?
?
?
Execution of a "select" statement proceeds in several steps:
1、For all the cases in the statement, the channel operands of receive operations and the channel and right-hand-side expressions of send statements are evaluated exactly once, in source order, upon entering the "select" statement.(所有channel表達(dá)式都會(huì)被求值、所有被發(fā)送的表達(dá)式都會(huì)被求值。求值順序:自上而下、從左到右)
2、If one or more of the communications can proceed, a single one that can proceed is chosen via a uniform pseudo-random selection. Otherwise, if there is a default case, that case is chosen. If there is no default case, the "select" statement blocks until at least one of the communications can proceed.(如果有一個(gè)或多個(gè)IO操作可以完成,則Go運(yùn)行時(shí)系統(tǒng)會(huì)隨機(jī)的選擇一個(gè)執(zhí)行,否則的話,如果有default分支,則執(zhí)行default分支語(yǔ)句,如果連default都沒有,則select語(yǔ)句會(huì)一直阻塞,直到至少有一個(gè)IO操作可以進(jìn)行)
3、Unless the selected case is the default case, the respective communication operation is executed.
4、If the selected case is a RecvStmt with a short variable declaration or an assignment, the left-hand side expressions are evaluated and the received value (or values) are assigned.
5、The statement list of the selected case is executed.
?
Since communication on?nil?channels can never proceed, a select with only?nil?channels and no default case blocks forever.
?
可以使用break語(yǔ)句來終止select語(yǔ)句的執(zhí)行。
?
示例1:select語(yǔ)句會(huì)一直等待,直到某個(gè)case里的IO操作可以進(jìn)行
//main.go
?
package main import "fmt" import "time" func f1(ch chan int) { ????time.Sleep(time.Second * 5) ????ch <- 1 } func f2(ch chan int) { ????time.Sleep(time.Second * 10) ????ch <- 1 } func main() { ????var ch1 = make(chan int) ????var ch2 = make(chan int) ????go f1(ch1) ????go f2(ch2) ????select { ????case <-ch1: ????????fmt.Println("The first case is selected.") ????case <-ch2: ????????fmt.Println("The second case is selected.") ????} }編譯運(yùn)行:
?
?
C:/go/bin/go.exe run test14.go [E:/project/go/proj/src/test]
The first case is selected.
成功: 進(jìn)程退出代碼 0.
?
示例2:所有跟在case關(guān)鍵字右邊的發(fā)送語(yǔ)句或接收語(yǔ)句中的通道表達(dá)式和元素表達(dá)式都會(huì)先被求值。無論它們所在的case是否有可能被選擇都會(huì)這樣。?
?
求值順序:自上而下、從左到右?
此示例使用空值channel進(jìn)行驗(yàn)證。
//main.go
?
package main import ( ????"fmt" ) //定義幾個(gè)變量,其中chs和numbers分別代表通道列表和整數(shù)列表 var ch1 chan int var ch2 chan int var chs = []chan int{ch1, ch2} var numbers = []int{1, 2, 3, 4, 5} func main() { ????select { ????case getChan(0) <- getNumber(2): ????????fmt.Println("1th case is selected.") ????case getChan(1) <- getNumber(3): ????????fmt.Println("2th case is selected.") ????default: ????????fmt.Println("default!.") ????} } func getNumber(i int) int { ????fmt.Printf("numbers[%d]\n", i) ????return numbers[i] } func getChan(i int) chan int { ????fmt.Printf("chs[%d]\n", i) ????return chs[i] }編譯運(yùn)行:
?
?
C:/go/bin/go.exe run test4.go [E:/project/go/proj/src/test]
chs[0]
numbers[2]
chs[1]
numbers[3]
default!.
成功: 進(jìn)程退出代碼 0.
上面的案例,之所以輸出default!.是因?yàn)閏hs[0]和chs[1]都是空值channel,和空值channel通信永遠(yuǎn)都不會(huì)成功。
?
示例3:所有跟在case關(guān)鍵字右邊的發(fā)送語(yǔ)句或接收語(yǔ)句中的通道表達(dá)式和元素表達(dá)式都會(huì)先被求值。無論它們所在的case是否有可能被選擇都會(huì)這樣。?
求值順序:自上而下、從左到右?
此示例使用非空值channel進(jìn)行驗(yàn)證。
//main.go
?
package main import ( ????"fmt" ) //定義幾個(gè)變量,其中chs和numbers分別代表通道列表和整數(shù)列表 var ch1 chan int = make(chan int, 1) //聲明并初始化channel變量 var ch2 chan int = make(chan int, 1) //聲明并初始化channel變量 var chs = []chan int{ch1, ch2} var numbers = []int{1, 2, 3, 4, 5} func main() { ????select { ????case getChan(0) <- getNumber(2): ????????fmt.Println("1th case is selected.") ????case getChan(1) <- getNumber(3): ????????fmt.Println("2th case is selected.") ????default: ????????fmt.Println("default!.") ????} } func getNumber(i int) int { ????fmt.Printf("numbers[%d]\n", i) ????return numbers[i] } func getChan(i int) chan int { ????fmt.Printf("chs[%d]\n", i) ????return chs[i] } 編譯運(yùn)行:C:/go/bin/go.exe run test4.go [E:/project/go/proj/src/test]
chs[0]
numbers[2]
chs[1]
numbers[3]
1th case is selected.
成功: 進(jìn)程退出代碼 0.
此示例,使用非空值channel進(jìn)行IO操作,所以可以成功,沒有走default分支。
示例4:如果有多個(gè)case同時(shí)可以運(yùn)行,go會(huì)隨機(jī)選擇一個(gè)case執(zhí)行 //main.go package main import ( ????"fmt" ) func main() { ????chanCap := 5 ????ch := make(chan int, chanCap) //創(chuàng)建channel,容量為5 ????for i := 0; i < chanCap; i++ { //通過for循環(huán),向channel里填滿數(shù)據(jù) ????????select { //通過select隨機(jī)的向channel里追加數(shù)據(jù) ????????case ch <- 1: ????????case ch <- 2: ????????case ch <- 3: ????????} ????} ????for i := 0; i < chanCap; i++ { ????????fmt.Printf("%v\n", <-ch) ????} }編譯運(yùn)行:
C:/go/bin/go.exe run test5.go [E:/project/go/proj/src/test]
2
1
2
1
1
成功: 進(jìn)程退出代碼 0.
注意:上面的案例每次運(yùn)行結(jié)果都不一樣。
?
示例5:使用break終止select語(yǔ)句的執(zhí)行
?
package main import "fmt" func main() { ????var ch = make(chan int, 1) ????ch <- 1 ????select { ????case <-ch: ????????fmt.Println("This case is selected.") ????????break //The following statement in this case will not execute. ????????fmt.Println("After break statement") ????default: ????????fmt.Println("This is the default case.") ????} ????fmt.Println("After select statement.") }?
?
?
編譯運(yùn)行:
?
C:/go/bin/go.exe run test15.go [E:/project/go/proj/src/test]
This case is selected.
After select statement.
成功: 進(jìn)程退出代碼 0.
《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的GO select用法详解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Dockerfile创建自定义Docke
- 下一篇: 全面学习Prometheus