Golang併發模型:輕鬆入門select

之前的文章都提到過,Golang的併發模型都來自生活,select也不例外。舉個例子:我們都知道一句話,“吃飯睡覺打豆豆”,這一句話裏包含了3件事:

  1. 媽媽喊你吃飯,你去吃飯。
  2. 時間到了,要睡覺。
  3. 沒事做,打豆豆。

在Golang裏,select就是幹這個事的:到吃飯了去吃飯,該睡覺了就睡覺,沒事幹就打豆豆。

結束髮散,我們看下select的功能,以及它能做啥。

select功能

在多個通道上進行讀或寫操作,讓函數可以處理多個事情,但1次只處理1個。以下特性也都必須熟記於心:

  1. 每次執行select,都會只執行其中1個case或者執行default語句。
  2. 當沒有case或者default可以執行時,select則阻塞,等待直到有1個case可以執行。
  3. 當有多個case可以執行時,則隨機選擇1個case執行。
  4. case後面跟的必須是讀或者寫通道的操作,否則編譯出錯。

select長下面這個樣子,由selectcase組成,default不是必須的,如果沒其他事可做,可以省略default

func main() {
    readCh := make(chan int, 1)
    writeCh := make(chan int, 1)

    y := 1
    select {
    case x := <-readCh:
        fmt.Printf("Read %d\n", x)
    case writeCh <- y:
        fmt.Printf("Write %d\n", y)
    default:
        fmt.Println("Do what you want")
    }
}

我們創建了readChwriteCh2個通道:

  1. readCh中沒有數據,所以case x := <-readCh讀不到數據,所以這個case不能執行。
  2. writeCh是帶緩衝區的通道,它裏面是空的,可以寫入1個數據,所以case writeCh <- y可以執行。
  3. case可以執行,所以default不會執行。

這個測試的結果是

$ go run example.go
Write 1

用打豆豆實踐select

來,我們看看select怎麼實現打豆豆:eat()函數會啓動1個協程,該協程先睡幾秒,事件不定,然後喊你吃飯,main()函數中的sleep是個定時器,每3秒喊你吃1次飯,select則處理3種情況:

  1. eatCh中讀到數據,代表有人喊我吃飯,我要吃飯了。
  2. sleep.C中讀到數據,代表鬧鐘時間到了,我要睡覺。
  3. default是,沒人喊我吃飯,也不到時間睡覺,我就打豆豆。
import (
    "fmt"
    "time"
    "math/rand"
)

func eat() chan string {
    out := make(chan string)
    go func (){
        rand.Seed(time.Now().UnixNano())
        time.Sleep(time.Duration(rand.Intn(5)) * time.Second)
        out <- "Mom call you eating"
        close(out)
    }()
    return out
}


func main() {
    eatCh := eat()
    sleep := time.NewTimer(time.Second * 3)
    select {
    case s := <-eatCh:
        fmt.Println(s)
    case <- sleep.C:
        fmt.Println("Time to sleep")
    default:
        fmt.Println("Beat DouDou")
    }
}

由於前2個case都要等待一會,所以都不能執行,所以執行default,運行結果一直是打豆豆:

$ go run x.go
Beat DouDou

現在我們不打豆豆了,你把default和下面的打印註釋掉,多運行幾次,有時候會吃飯,有時候會睡覺,比如這樣:

$ go run x.go
Mom call you eating
$ go run x.go
Time to sleep
$ go run x.go
Time to sleep

select很簡單但功能很強大,它讓golang的併發功能變的更強大。這篇文章寫的囉嗦了點,重點是爲下一篇文章做鋪墊,下一篇我們將介紹下select的高級用法。

select的應用場景很多,讓我總結一下,放在下一篇文章中吧。

併發系列文章推薦

  1. 如果這篇文章對你有幫助,請點個贊/喜歡,鼓勵我持續分享,感謝。
  2. 我的文章列表,點此可查看
  3. 如果喜歡本文,隨意轉載,但請保留此原文鏈接

一起學Golang-分享有料的Go語言技術

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章