Go開發 Channel 徹底研究(四)select基礎介紹

編程牛 發佈 2024-04-01T17:28:50.734738+00:00

為什麼需要select?有時會遇到這種情景:需要對多個channel進行監聽。如圖所示,就像一個人監聽多個通道一樣,假如採用for循環形式for{ d1,ok:=<-c1 //... d2,ok:=<-c2 //... ....

為什麼需要select?

有時會遇到這種情景:需要對多個channel進行監聽。

如圖所示,就像一個人監聽多個通道一樣,假如採用for循環形式

for{
    d1,ok:=<-c1
    //...
    d2,ok:=<-c2
    //...
    ....
}

這個方式肯定走不通,原因是一旦某個通道監聽阻塞了,下面的部分都不會執行到。此外,即使使用ok狀態判斷不阻塞,但同時也會使for空轉,一下就將CPU拉升到90%,性能很差。

那麼有沒有一種方法,能像治安巡查一樣一輪輪的輪詢,而且能自動阻塞,就可以解決這個問題了。

go提供了select,正對應輪詢的思路,模式如下:

select{
    case xx:
    case xx:
    case xx:
    default:xx
}

運行過程

從上往下「巡查」,如果發現哪個case處於可執行狀態,就執行該條語句,那麼其餘語句就不執行了。

如果都不能執行,且有default語句時,就執行default

如果沒有default語句,那麼select整個就會阻塞(導致所在協程阻塞),直到解除。

但有人會有疑問,上面這些語句不是只能輪詢一次嗎?這個容易解決,我們給select外層再加一個for循環,這樣就可以無限的輪詢。

for{
    select{
        case xx:
        case xx:
        ........
    }
}

模式基本形成了。

客戶和服務端交互模擬

下面先看一個基礎的例子,主要用來模仿客戶和服務端的交互,模型如下:

ch := make(chan string)

//模擬啟動服務端
go func(ch chan string) {
        for {
                data := <-ch
                fmt.Println("服務端接收到數據:", data)
                time.Sleep(time.Second * 2)
                //'roger'表示信息收到的意思..
                ch <- "roger"
        }
}(ch)

//模擬客戶端一次請求
ch <- "hello,服務端!"
select {
case ack := <-ch:
        fmt.Println(ack)
case <-time.After(time.Second):
        fmt.Println("返回超時...")

}

分析

  1. 服務端需要持續服務,因此採用for無限循環形式
  2. 客戶端的返回值就是和超時進行速度PK。

總結

介紹了select的基本原理,下篇說明選擇規則

關鍵字: