select的用法與switch非常類似,由select開始一個(gè)新的選擇塊,每個(gè)選擇條件由case語(yǔ)句來(lái)描述。與switch語(yǔ)句可以選擇任何可使用相等比較的條件相比,select有比較多的限制,其中最大的一條限制就是每個(gè)case語(yǔ)句里必須是一個(gè)IO操作,確切的說(shuō),應(yīng)該是一個(gè)面向channel的IO操作。
在執(zhí)行select語(yǔ)句的時(shí)候,運(yùn)行時(shí)系統(tǒng)會(huì)自上而下地判斷每個(gè)case中的發(fā)送或接收操作是否可以被立即執(zhí)行(立即執(zhí)行:意思是當(dāng)前Goroutine不會(huì)因此操作而被阻塞)。
select語(yǔ)法格式
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.
select {
????caseSendStmt:
????????//statements
????caseRecvStmt:
????????//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!.")
}
示例1:select語(yǔ)句會(huì)一直等待,直到某個(gè)case里的IO操作可以進(jìn)行
func fun1(ch chan int) {
????time.Sleep(time.Second * 5)
????ch <- 1
}
func fun2(ch chan int) {
????time.Sleep(time.Second * 10)
????ch <- 1
}
func main() {
????var ch1 = make(chan int)
????var ch2 = make(chan int)
????go fun1(ch1)
????go fun2(ch2)
????select {
????case <-ch1:
????????fmt.Println("The first case is selected.")
????case <-ch2:
????????fmt.Println("The second case is selected.")
????}
}
編譯運(yùn)行:The first case is selected.
示例2:使用空值channel進(jìn)行驗(yàn)證
所有跟在case關(guān)鍵字右邊的發(fā)送語(yǔ)句或接收語(yǔ)句中的通道表達(dá)式和元素表達(dá)式都會(huì)先被求值。無(wú)論它們所在的case是否有可能被選擇都會(huì)這樣。?
求值順序:自上而下、從左到右
//定義幾個(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)行:
chs[0]
numbers[2]
chs[1]
numbers[3]
default!.
之所以輸出default!,是因?yàn)閏hs[0]和chs[1]都是空值channel,和空值channel通信永遠(yuǎn)都不會(huì)成功。
示例3:使用非空值channel進(jìn)行驗(yàn)證
//定義幾個(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)行:
chs[0]
numbers[2]
chs[1]
numbers[3]
1th case is selected.
使用非空值channel進(jìn)行IO操作,所以可以成功,沒有走default分支。
示例4:如果有多個(gè)case同時(shí)可以運(yùn)行,go會(huì)隨機(jī)選擇一個(gè)case執(zhí)行
func main() {
????chanCap := 5
????ch := make(chan int, chanCap) //創(chuàng)建channel,容量為5
????for i := 0; i < chanCap; i++ { //通過(guò)for循環(huán),向channel里填滿數(shù)據(jù)
????????select { //通過(guò)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)行:
2
1
2
1
1
示例5:使用break終止select語(yǔ)句的執(zhí)行
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)行:
This case is selected.
After select statement.