Golang的select多路復用及channel使用操作
看到有個例子實現瞭一個類似於核彈發射裝置,在發射之前還是需要隨時能輸入終止發射。
這裡就可以用到cahnnel 配合select 實現多路復用。
select的寫法用法有點像switch。但是和switch不同的是,select的一個case代表一個通信操作(在某個channel上進行發送或者接收)並且會包含一些語句組成的一個語句塊。現在讓我們來實現一下這個核彈發射器
package main import ( "fmt" "time" "os" ) func launch() { fmt.Println("nuclear launch detected") } func commencingCountDown(canLunch chan int) { c := time.Tick(1 * time.Second) for countDown := 20; countDown > 0; countDown-- { fmt.Println(countDown) <- c } canLunch <- -1 } func isAbort(abort chan int) { os.Stdin.Read(make([]byte, 1)) abort <- -1 } func main() { fmt.Println("Commencing coutdown") abort := make(chan int) canLunch := make(chan int) go isAbort(abort) go commencingCountDown(canLunch) select { case <- canLunch: case <- abort: fmt.Println("Launch aborted!") return } launch() }
首先打印瞭一個commencing countdown開始進行倒數計時。
申明一個int類型的 channel變量abort 用來做取消時候傳遞給select的消息信號量這個後面會介紹到。
申明一個int類型的 channel變量canLunch 用來做倒計時結束可以發射的信號量。 隻有當倒數結束,且canLunch有值後才能進行發射。
用一個goroutine開啟一個用於監聽是否有停止發射信號的函數isAbort並且把申明好的channel變量傳入。
isAbort就幹一件事情,監聽是否有標準輸入輸入,如果有輸入我們默認是下達瞭發射停止的信號 需要向abort channel裡面發送一個信號。這裡我們會發射一個-1
用一個goroutine開啟一個用於倒數計時的函數commencingCountDown負責開始倒計時,這裡重新申明瞭一個 TICK channel 每一秒倒數計時一下。並且在倒數計時完成之後向canLunch channel發送信號。
然後開始執行select,select在沒有就緒的channel的時候會阻塞或者執行指定的defualt,這裡我沒有寫default所以他會阻塞監聽兩個信號,一個是canLunch,一個是停止發送。隻要收到任何一個信號後,執行該信號後面的內容
最後運行Lunch函數。
其實把思路理清楚,以並發的思考方式去思考這類問題感覺還是不會太亂。多加練習應該會變好。下面的文章應該會開始逐步開始從服務器和連接開始,實現一個im系統。或者添加更多的實踐。
補充:golang 使用select完成超時
我就廢話不多說瞭,大傢還是直接看代碼吧~
timeout := make(chan bool, 1) go func() { time.Sleep(1e9) timeout <- true } () select { case <- ch: //從ch中讀取數據 case <-timeout: //ch一直沒有數據寫入,超時觸發timeout }
func main() { var a chan string a =make(chan string) go sendDataTo(a) go timing() getAchan(10*time.Second,a) } func sendDataTo(a chan string) { for { a <- "我是a通道的數據" time.Sleep(1e9 *3) } } //在一定時間內接收不到a的數據則超時 func getAchan(timeout time.Duration, a chan string) { var after <-chan time.Time loop: after = time.After(timeout) for{ fmt.Println("等待a中的數據,10秒後沒有數據則超時") select { case x :=<- a: fmt.Println(x) goto loop case <-after: fmt.Println("timeout.") return } } } func timing() { //定時器,10秒鐘執行一次 ticker := time.NewTicker(10 * time.Second) for { time := <-ticker.C fmt.Println("定時器====>",time.String()) } }
以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。如有錯誤或未考慮完全的地方,望不吝賜教。
推薦閱讀:
- Go緩沖channel和非緩沖channel的區別說明
- Go select使用與底層原理講解
- go語言中如何使用select的實現示例
- go select的用法
- golang coroutine 的等待與死鎖用法