golang 中的 time 包的 Ticker

Posted 星火燎原智勇

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了golang 中的 time 包的 Ticker相关的知识,希望对你有一定的参考价值。

真实的应用场景是:在测试收包的顺序的时候,加了个 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的主要内容,如果未能解决你的问题,请参考以下文章

golang中定时器实现,Ticker,Timer

Golang如何正确的停止Ticker

Golang——time.Ticker定时器

go语言time包的学习(Time,Location,Duration,Timer,Ticker)

golang 中timer,ticker 的使用

Ticker 在 time.sleep() 中的行为