select是什么
? ?select
是Go語言層面提供的一種多路復用機制,用于檢測當前goroutine
連接的多個channel
是否有數據準備完畢,可用于讀或寫。
????????Go語言的select
語句,是用來起一個goroutine
監聽多個Channel
的讀寫事件,提高從多個Channel
獲取信息的效率,相當于是單線程處理多個IO事件,其思想基本相同。
select的用法
select
的基本使用模式如下:
select {case <- channel1: // 如果從channel1讀取數據成功,執行case語句 do ... case channel2 <- 1: // 如果向channel2寫入數據成功,執行case語句 do ... default: // 如果上面都沒有成功,進入default處理流程do ...
}
????????可以看到,select
的用法形式類似于switch
,但是區別于switch
的是,select
各個case
的表達式必須都是channel
的讀寫操作。select
通過多個case
語句監聽多個channel
的讀寫操作是否準備好可以執行,其中任何一個case
可以執行了則選擇該case
語句執行,如果沒有可以執行的case
,則執行default
語句,如果沒有default
,則當前goroutine
會阻塞。?
空select永久阻塞
當一個select
中什么語句都沒有,沒有任何case
,將會永久阻塞:
package mainfunc main() {select {}
}
運行結果:
fatal error: all goroutines are asleep - deadlock!
????????程序因為select
語句導致永久阻塞,當前goroutine
阻塞之后,由于Go語言自帶死鎖檢測機制,發現當前goroutine
永遠不會被喚醒,會報上述死鎖錯誤。?
沒有default且case無法執行的select永久阻塞
看下面示例:
package mainimport ("fmt"
)func main() {ch1 := make(chan int, 1)ch2 := make(chan int, 1)select {case <-ch1:fmt.Printf("received from ch1")case num := <-ch2:fmt.Printf("num is: %d", num)}
}
運行結果:
fatal error: all goroutines are asleep - deadlock!
????????程序中?select
從兩個channel
,ch1
和ch2
中讀取數據,但是兩個channel
都沒有數據,且沒有goroutine
往里面寫數據,所以不可能讀到數據,這兩個case
永遠無法執行到,select
也沒有default
,所以會出現永久阻塞,報死鎖。?
單一case和default的select
package mainimport ("fmt"
)func main() {ch := make(chan int, 1)select {case <-ch:fmt.Println("received from ch")default:fmt.Println("default!!!")}
}
運行結果:
default!!!
????????執行到select
語句的時候,由于ch
中沒有數據,且沒有goroutine
往channel
中寫數據,所以case
不可能執行到,就會執行default
語句,打印出default!!!
。?
?多個case和default的select
package mainimport ("fmt""time"
)func main() {ch1 := make(chan int, 1)ch2 := make(chan int, 1)go func() {time.Sleep(time.Second)for i := 0; i < 3; i++ {select {case v := <-ch1:fmt.Printf("Received from ch1, val = %d\n", v)case v := <-ch2:fmt.Printf("Received from ch2, val = %d\n", v)default:fmt.Println("default!!!")}time.Sleep(time.Second)}}()ch1 <- 1time.Sleep(time.Second)ch2 <- 2time.Sleep(4 * time.Second)
}
運行結果:
Received from ch1, val = 1
Received from ch2, val = 2
default!!!
????????主goroutine
中向后往管道ch1
和ch2
中發送數據,在子goroutine
中執行兩個select
,可以看到,在執行select
的時候,那個case
準備好了就會執行當下case
的語句,最后沒有數據可接受了,沒有case
可以執行,則執行default
語句。
注意:當多個case都準備好了的時候,會隨機選擇一個執行
package mainimport ("fmt"
)func main() {ch1 := make(chan int, 1)ch2 := make(chan int, 1)ch1 <- 5ch2 <- 6select {case v := <-ch1:fmt.Printf("Received from ch1, val = %d\n", v)case v := <-ch2:fmt.Printf("Received from ch2, val = %d\n", v)default:fmt.Println("default!!!")}
}
運行結果:
Received from ch2, val = 6
多次執行,2個case
都有可能打印,這就是select
選擇的隨機性。