定義:在golang裏頭select的功能與epoll(nginx)/poll/select的功能相似,都是堅挺IO操做,當IO操做發生的時候,觸發相應的動做。nginx
在Go的語言規範中,select中的case的執行順序是隨機的,當有多個case均可以運行,select會隨機公平地選出一個執行,其餘的便不會執行:golang
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 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 }
也能夠這麼寫:dom
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是否已經滿了)ide
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 }
select-case中的chan操做編譯成了if-else。如:ui
1 select { 2 case v = <-c: 3 ...foo 4 default: 5 ...bar 6 }
會被編譯爲:spa
1 if selectnbrecv(&v, c) { 2 ...foo 3 } else { 4 ...bar 5 }
相似地code
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亂序了。
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 }
If multiple cases can proceed, a uniform pseudo-random choice is made to decide which single communication will execute.
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 }
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 }