這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
select
golang的select
功能和C中的select, poll, epoll
類似,就是監聽 IO 操作,當 IO 操作發生時,觸發相應的動作。
ch1 := make (chan int, 1)ch2 := make (chan int, 1)select {case <-ch1: fmt.Println("ch1 pop one element")case <-ch2: fmt.Println("ch2 pop one element")}
select 的代碼形式和 switch 非常相似, 不過 select 的 case 裡的動作陳述式只能是【IO 操作】 。
此樣本裡面 select 會一直等待等到某個 case 陳述式完成, 也就是等到成功從 ch1 或者 ch2 中讀到資料。 則 select 語句結束。
使用 select 實現 timeout 機制
timeout := make (chan bool, 1)go func() { time.Sleep(1e9) // sleep one second timeout <- true}()ch := make (chan int) select {case <- ch:case <- timeout: fmt.Println("timeout!")}
同時等待讀取ch和timeout,當逾時時間到的時候,<- timeout
會操作成功。 所以 select 語句則會退出。 而不是一直阻塞在 ch 的讀取操作上。 從而實現了對 ch 讀取操作的逾時設定。
如果select包含default
,則上述兩個都無法成功讀取的情況下,直接進入default
。因此,可以在select中放入case ch <- 1
,如果進入default
條件,說明通道已經滿了。
注意點
- 沒有default時,select語句會一直等待,直到某個case裡的IO操作可以進行
- case條件中包含的【通道運算式】和【元素運算式】都會先被求值。無論它們所在的case是否有可能被選擇都會這樣。
- 如果有多個case同時可以運行,go會隨機播放一個case執行
switch
golang中switch有兩種判斷條件:運算式、類型(boolean-expression or integral type)
switch marks { case 90: grade = "A" case 80: grade = "B" case 50,60,70 : grade = "C" default: grade = "D" }switch { case grade == "A" : fmt.Printf("Excellent!\n" ) case grade == "B", grade == "C" : fmt.Printf("Well done\n" ) case grade == "D" : fmt.Printf("You passed\n" ) case grade == "F": fmt.Printf("Better try again\n" ) default: fmt.Printf("Invalid grade\n" );}switch i := x.(type) { case nil: fmt.Printf("type of x :%T",i) case int: fmt.Printf("x is int") case func(int) float64: fmt.Printf("x is func(int)") case bool, string: fmt.Printf("x is bool or string") default: fmt.Printf("don't know the type") }
使用select + switch實現 goroutine 控制
通過在一個goroutine中發送控制訊號控制其他goroutine的運行狀態,如暫停和運行
package mainimport ( "fmt" "runtime" "sync")// Possible worker states.const ( Stopped = 0 Paused = 1 Running = 2)// Maximum number of workers.const WorkerCount = 1000func main() { // Launch workers. var wg sync.WaitGroup wg.Add(WorkerCount + 1) workers := make([]chan int, WorkerCount) for i := range workers { workers[i] = make(chan int, 1) go func(i int) { worker(i, workers[i]) wg.Done() }(i) } // Launch controller routine. go func() { controller(workers) wg.Done() }() // Wait for all goroutines to finish. wg.Wait()}func worker(id int, ws <-chan int) { state := Paused // Begin in the paused state. for { select { case state = <-ws: switch state { case Stopped: fmt.Printf("Worker %d: Stopped\n", id) return case Running: fmt.Printf("Worker %d: Running\n", id) case Paused: fmt.Printf("Worker %d: Paused\n", id) } default: // We use runtime.Gosched() to prevent a deadlock in this case. // It will not be needed of work is performed here which yields // to the scheduler. runtime.Gosched() if state == Paused { break } // Do actual work here. } }}// controller handles the current state of all workers. They can be// instructed to be either running, paused or stopped entirely.func controller(workers []chan int) { // Start workers setState(workers, Running) // Pause workers. setState(workers, Paused) // Unpause workers. setState(workers, Running) // Shutdown workers. setState(workers, Stopped)}// setState changes the state of all given workers.func setState(workers []chan int, state int) { for _, w := range workers { w <- state }}
注意,此處有坑!!!
使用該方法有可能導致程式在啟動時死結,並不能在controller中隨意的暫停和啟動。比如,一開始寫入 暫停 ,此時worker暫停,導致不能從worker chan中消費資料,導致controller不能向worker chan寫入run狀態(chan長度為1)
因此,在程式第一次執行到此處時,要注意對chan的控制