1. select的使用
定義:在golang裡頭select的功能與epoll(nginx)/poll/select的功能類似,都是堅挺IO操作,當IO操作發生的時候,觸發相應的動作。
1.1 一些使用規範
在Go的語言規範中,select中的case的執行順序是隨機的,當有多個case都可以運行,select會隨機公平地選出一個執行,其他的便不會執行:
1 package main
2
3 import "fmt"
4
5 func main() {
6 ch := make (chan int, 1)
7
8 ch<-1
9 select {
10 case <-ch:
11 fmt.Println("隨機一")
12 case <-ch:
13 fmt.Println("隨機二n")
14 }
15 }
輸出內容為隨機一二里面的任意一個。
case後面必須是channel操作,否則報錯;default子句總是可運行的,所以沒有default的select才會阻塞等待事件 ;沒有運行的case,那麼將會阻塞事件發生報錯(死鎖)。
1.2 select的應用場景
timeout 機制(超時判斷)
1 package main
2
3 import (
4 "fmt"
5 "time"
6 )
7
8 func main() {
9 timeout := make (chan bool, 1)
10 go func() {
11 time.Sleep(1*time.Second) // 休眠1s,如果超過1s還沒I操作則認為超時,通知select已經超時啦~
12 timeout <- true
13 }()
14 ch := make (chan int)
15 select {
16 case <- ch:
17 case <- timeout:
18 fmt.Println("超時啦!")
19 }
20 }
也可以這麼寫:
1 package main
2
3 import (
4 "fmt"
5 "time"
6 )
7
8 func main() {
9 ch := make (chan int)
10 select {
11 case <-ch:
12 case <-time.After(time.Second * 1): // 利用time來實現,After代表多少時間后執行輸出東西
13 fmt.Println("超時啦!")
14 }
15 }
判斷channel是否阻塞(或者說channel是否已經滿了)
1 package main
2
3 import (
4 "fmt"
5 )
6
7 func main() {
8 ch := make (chan int, 1) // 注意這裏給的容量是1
9 ch <- 1
10 select {
11 case ch <- 2:
12 default:
13 fmt.Println("通道channel已經滿啦,塞不下東西了!")
14 }
15 }
退出機制
1 package main
2
3 import (
4 "fmt"
5 "time"
6 )
7
8 func main() {
9 i := 0
10 ch := make(chan string, 0)
11 defer func() {
12 close(ch)
13 }()
14
15 go func() {
16 DONE:
17 for {
18 time.Sleep(1*time.Second)
19 fmt.Println(time.Now().Unix())
20 i++
21
22 select {
23 case m := <-ch:
24 println(m)
25 break DONE // 跳出 select 和 for 循環
26 default:
27 }
28 }
29 }()
30
31 time.Sleep(time.Second * 4)
32 ch<-"stop"
33 }
2. select的實現
select-case中的chan操作編譯成了if-else。如:
1 select {
2 case v = <-c:
3 ...foo
4 default:
5 ...bar
6 }
會被編譯為:
1 if selectnbrecv(&v, c) {
2 ...foo
3 } else {
4 ...bar
5 }
類似地
1 select {
2 case v, ok = <-c:
3 ... foo
4 default:
5 ... bar
6 }
會被編譯為:
1 if c != nil && selectnbrecv2(&v, &ok, c) {
2 ... foo
3 } else {
4 ... bar
5 }
selectnbrecv函數只是簡單地調用runtime.chanrecv函數,不過是設置了一個參數,告訴當runtime.chanrecv函數,當不能完成操作時不要阻塞,而是返回失敗。也就是說,所有的select操作其實都僅僅是被換成了if-else判斷,底層調用的不阻塞的通道操作函數。
在Go的語言規範中,select中的case的執行順序是隨機的,那麼,如何實現隨機呢?
select和case關鍵字使用了下面的結構體:
1 struct Scase
2 {
3 SudoG sg; // must be first member (cast to Scase)
4 Hchan* chan; // chan
5 byte* pc; // return pc
6 uint16 kind;
7 uint16 so; // vararg of selected bool
8 bool* receivedp; // pointer to received bool (recv2)
9 };
1 struct Select
2 {
3 uint16 tcase; // 總的scase[]數量
4 uint16 ncase; // 當前填充了的scase[]數量
5 uint16* pollorder; // case的poll次序
6 Hchan** lockorder; // channel的鎖住的次序
7 Scase scase[1]; // 每個case會在結構體里有一個Scase,順序是按出現的次序
8 };
每個select都對應一個Select結構體。在Select數據結構中有個Scase數組,記錄下了每一個case,而Scase中包含了Hchan。然後pollorder數組將元素隨機排列,這樣就可以將Scase亂序了。
3. select死鎖
select不注意也會發生死鎖,分兩種情況:
如果沒有數據需要發送,select中又存在接收通道數據的語句,那麼將發送死鎖
1 package main
2 func main() {
3 ch := make(chan string)
4 select {
5 case <-ch:
6 }
7 }
預防的話加default。
空select,也會引起死鎖。
1 package main
2
3 func main() {
4 select {}
5 }
4. select和switch的區別
select
select只能應用於channel的操作,既可以用於channel的數據接收,也可以用於channel的數據發送。如果select的多個分支都滿足條件,則會隨機的選取其中一個滿足條件的分支, 如規範中所述:
If multiple cases can proceed, a uniform pseudo-random choice is made to decide which single communication will execute.
`case`語句的表達式可以為一個變量或者兩個變量賦值。有default語句。
31 package main 32 import "time"
33 import "fmt"
35 func main() { 36 c1 := make(chan string)
37 c2 := make(chan string) 38 go func() {
39 time.Sleep(time.Second * 1) 40 c1 <- "one"
41 }() 42 go func() {
43 time.Sleep(time.Second * 2) 44 c2 <- "two"
45 }() 46 for i := 0; i < 2; i++ {
47 select { 48 case msg1 := <-c1:
49 fmt.Println("received", msg1)
50 case msg2 := <-c2:
51 fmt.Println("received", msg2)
52 }
53 }
switch
switch可以為各種類型進行分支操作, 設置可以為接口類型進行分支判斷(通過i.(type))。switch 分支是順序執行的,這和select不同。
1 package main
2 import "fmt"
3 import "time"
4
5 func main() {
6 i := 2
7 fmt.Print("Write ", i, " as ")
8 switch i {
9 case 1:
10 fmt.Println("one")
11 case 2:
12 fmt.Println("two")
13 case 3:
14 fmt.Println("three")
15 }
16 switch time.Now().Weekday() {
17 case time.Saturday, time.Sunday:
18 fmt.Println("It's the weekend")
19 default:
20 fmt.Println("It's a weekday")
21 }
22 t := time.Now()
23 switch {
24 case t.Hour() < 12:
25 fmt.Println("It's before noon")
26 default:
27 fmt.Println("It's after noon")
28 }
29 whatAmI := func(i interface{}) {
30 switch t := i.(type) {
31 case bool:
32 fmt.Println("I'm a bool")
33 case int:
34 fmt.Println("I'm an int")
35 default:
36 fmt.Printf("Don't know type %T\n", t)
37 }
38 }
39 whatAmI(true)
40 whatAmI(1)
41 whatAmI("hey")
42 }
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※想知道網站建置、網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計及後台網頁設計
※不管是台北網頁設計公司、台中網頁設計公司,全省皆有專員為您服務
※Google地圖已可更新顯示潭子電動車充電站設置地點!!
※帶您來看台北網站建置,台北網頁設計,各種案例分享
※小三通物流營運型態?
※快速運回,大陸空運推薦?