Go入門26:select的幾種用法

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.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • golang 的 select 的功能和select, poll, epoll相似, 就是監(jiān)聽 IO 操作,當(dāng) I...
    Uzero閱讀 1,442評(píng)論 0 0
  • 01.{ 換行: Opening Brace Can't Be Placed on a Separate Lin...
    碼農(nóng)不器閱讀 2,477評(píng)論 0 14
  • 并發(fā)基礎(chǔ) 在說(shuō)Golang的并發(fā)編程之前,先認(rèn)識(shí)一下目前并發(fā)的幾種實(shí)現(xiàn)方式: 1.多進(jìn)程。操作系統(tǒng)實(shí)現(xiàn)的并發(fā)模型,...
    睡著別叫醒我閱讀 2,336評(píng)論 0 1
  • fmt格式化字符串 格式:%[旗標(biāo)][寬度][.精度][arg索引]動(dòng)詞旗標(biāo)有以下幾種:+: 對(duì)于數(shù)值類型總是輸出...
    皮皮v閱讀 1,221評(píng)論 0 3
  • 一部新近的老片兒 絕地逃亡 成龍主演 所以在電影院見到這個(gè) 貌似俄羅斯套娃般的他 很傳神 看來(lái)電影院這個(gè)地方 以后...
    老偉漫眼閱讀 216評(píng)論 0 2

友情鏈接更多精彩內(nèi)容