Golang併發模型:輕鬆入門select
之前的文章都提到過,Golang的併發模型都來自生活,select也不例外 。舉個例子:我們都知道一句話,“吃飯睡覺打豆豆”,這一句話裡包含了3件事:
- 媽媽喊你吃飯,你去吃飯。
- 時間到了,要睡覺。
- 沒事做,打豆豆。
在Golang裡,select就是幹這個事的:到吃飯了去吃飯,該睡覺了就睡覺,沒事幹就打豆豆。
結束髮散,我們看下select的功能,以及它能做啥。
select功能
在多個通道上進行讀或寫操作,讓函式可以處理多個事情,但1次只處理1個。以下特性也都必須熟記於心:
case
select長下面這個樣子,由select
和case
組成,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") } }
我們建立了readCh
和writeCh
2個通道:
-
readCh
中沒有資料,所以case x := <-readCh
讀不到資料,所以這個case不能執行。 -
writeCh
是帶緩衝區的通道,它裡面是空的,可以寫入1個數據,所以case writeCh <- y
可以執行。 -
有
case
可以執行,所以default
不會執行。
這個測試的結果是
$ go run example.go Write 1
用打豆豆實踐select
來,我們看看select怎麼實現打豆豆:eat()
函式會啟動1個協程,該協程先睡幾秒,事件不定,然後喊你吃飯,main()
函式中的sleep
是個定時器,每3秒喊你吃1次飯,select
則處理3種情況:
eatCh sleep.C 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的高階用法。