[Go 教程系列筆記] Select

目錄

什麼是 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 準備就緒。在 server1server2 分別是睡眠 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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章