目錄
- Go 教程系列筆記 Interface 第一部分
- Go 教程系列筆記 Interface 第二部分
- Go 教程系列筆記 併發介紹
- Go 教程系列筆記 goroutine(協程)
- Go 教程系列筆記 Channel 通道
- Go 教程系列筆記 緩衝通道和工作池
- Go 教程系列筆記 Select
- Go 教程系列筆記 Mutex(互斥鎖)
- Go 教程系列筆記 結構而不是類-Go中的OOP
- Go 教程系列筆記 組合而不是繼承-Go 中的 OOP
- Go 教程系列筆記 多態-Go 中的 OOP
什麼是 select ?
select
語句用於從多個發送/接收操作通道中進行選擇。select 語句是阻塞的,直到其中一個發送/接收操作準備就緒。如果多個操作都準備好,則隨機選擇其中一個操作。語法類似 switch
除了每個 case 語句都是通道操作。
下面讓我們來看一個例子以便更好的理解。
<!-- more -->
func server1(ch chan string) {
time.Sleep(6 * time.Second)
ch <- "from server1"
}
func server2(ch chan string) {
time.Sleep(3 * time.Second)
ch <- "from server2"
}
func main() {
output1 := make(chan string)
output2 := make(chan string)
go server1(output1)
go server2(output2)
select {
case s1 := <-output1:
fmt.Println(s1)
case s2 := <-output2:
fmt.Println(s2)
}
}
在上面的例子中,select 語句將阻塞着,直到其中一個 case 準備就緒。在 server1
和 server2
分別是睡眠 6 和 3 秒。因此 select 將阻塞 3 秒後程序進行打印。
實際使用選擇
讓我們假設我們有一個關鍵任務應用程序,我們需要儘快將結果返回給用戶。該應用程序的數據被複制並存儲在世界各地的不同服務器中。假設功能server1和server2實際上與2個這樣的服務器通信。每個服務器的響應時間取決於每個服務器的負載和網絡延遲。我們將請求發送到兩個服務器,然後使用該select語句在相應的通道上等待響應。首先響應的服務器由select選擇,其他響應被忽略。這樣我們就可以向多個服務器發送相同的請求,並將最快的響應返回給用戶:)。
默認情況 default
select
當其他 case 都沒有準備好時,將執行 default
語句。這通常用於防止 select 語句阻塞。
func main() {
select {
default:
// do something
}
}
Deadlock 和 default case
func main() {
ch := make(chan string)
select {
case <-ch:
}
}
當 select 語句永遠阻塞,沒有其他 goroutine 寫入此通道,因此將導致死鎖。
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan receive]:
main.main()
/tmp/sandbox416567824/main.go:6 +0x80
如果存在默認情況,則不會發生死鎖的情況,因此在沒有其他 case 準備就緒時將執行默認情況。
func main() {
ch := make(chan string)
select {
case <-ch:
default:
fmt.Println("default case executed")
}
}
隨機選擇
當 select
語句中的多個案例準備就緒時,將隨機執行一個 case。
空 select
func main() {
select {}
}
您認爲上述計劃的輸出結果如何?
我們知道select語句將被阻塞,直到其中一個案例被執行。在這種情況下,select語句沒有任何情況,因此它將永遠阻塞,從而導致死鎖。這個程序會因以下輸出而感到恐慌:
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [select (no cases)]:
main.main()
/tmp/sandbox299546399/main.go:4 +0x20