golang 中的 time 包的 Ticker
阿新 • • 發佈:2017-10-05
target spa tro int select con www 結果 div
真實的應用場景是:在測試收包的順序的時候,加了個 tick 就發現丟包了
那麽來看一個應用例子:
package main import ( "fmt" "runtime" "time" ) func init() { runtime.GOMAXPROCS(runtime.NumCPU()) } func main() { ch := make(chan int, 1024) go func(ch chan int) { for { val := <-ch fmt.Printf("val:%d\n", val) } }(ch) tick := time.NewTicker(1 * time.Second) for i := 0; i < 20; i++ { select { case ch <- i: case <-tick.C: fmt.Printf("%d: case <-tick.C\n", i) } time.Sleep(200 * time.Millisecond) } close(ch) tick.Stop() }
輸出結果如下:
val:0 val:1 val:2 val:3 val:4 val:5 6: case <-tick.C val:7 val:8 val:9 10: case <-tick.C val:11 val:12 val:13 val:14 15: case <-tick.C val:16 val:17 val:18 val:19
問題出在這個select裏面:
select { case ch <- i: case <-tick.C: fmt.Printf("%d: case <-tick.C\n", i) }
[tick.C 介紹說明] 當兩個 case 條件都滿足的時候,運行時系統會通過一個偽隨機的算法決定哪個case將會被執行。所以當 tick.C 條件滿足的那個循環,有某種概率造成 ch<-i 沒有發送(雖然通道兩端沒有阻塞,滿足發送條件)
解決方案1:一旦 tick.C 隨機的 case 被隨機到,就多執行一次 ch<-i (不體面,如果有多個case就不通用了)
select { case ch <- i: case <-tick.C: fmt.Printf("%d: case <-tick.C\n", i) ch <- i }
解決方案2:將tick.C的case單獨放到一個select裏面,並加入一個default(保證不阻塞)
select { case ch <- i: } select { case <-tick.C: fmt.Printf("%d: case <-tick.C\n", i) default: }
兩種解決方案的輸出都是希望的結果:
val:0 val:1 val:2 val:3 val:4 5: case <-tick.C val:5 val:6 val:7 val:8 val:9 10: case <-tick.C val:10 val:11 val:12 val:13 val:14 15: case <-tick.C val:15 val:16 val:17 val:18 val:19
golang 中的 time 包的 Ticker