为了账号安全,请及时绑定邮箱和手机立即绑定

Golang time.Ticker 阻塞一段时间后触发两次

Golang time.Ticker 阻塞一段时间后触发两次

Go
宝慕林4294392 2022-11-08 16:58:23
我正在使用time.Ticker一些阻塞代码,发现一个奇怪的行为:func main() {    ticker := time.NewTicker(1 * time.Second)    i := 0    for {        select {        case <-ticker.C:            log.Println(i)            if i == 0 {                time.Sleep(5 * time.Second) // simulate the blocking            }            i++        }    }}示例输出:2022/02/20 10:49:45 02022/02/20 10:49:50 1 <- shows at same time2022/02/20 10:49:50 2 <- shows at same time2022/02/20 10:49:51 32022/02/20 10:49:52 42022/02/20 10:49:53 5...“1”和“2”总是同时显示,这是为什么呢?
查看完整描述

2 回答

?
动漫人物

TA贡献1815条经验 获得超10个赞

代码创建一个容量为 1的缓冲通道。当通道已满时,自动收报机会丢弃时间值。

这是问题中跟踪的时间表。同一时间列出的事件按列出的顺序依次发生一点点。

1s - ticker sends value

1s - main goroutine receives first value

1s - main goroutine starts sleep for 5s

2s - ticker sends value (channel has capacity 1).

3s - ticker fails to send value because channel is full

4s - ticker fails to send value because channel is full

5s - ticker fails to send value because channel is full

6s - main goroutine exits sleep and receives buffered value

6s - ticker sends value

6s - main goroutine receives buffered value

ticker 和 main goroutine 跑到 6s 标记,因为 sleep 是 ticker 周期的倍数。在您的跟踪中,主 goroutine 赢得了比赛。在我的机器上,自动收报机赢得了比赛,我得到了你期望的时间。这是我机器上的样子。


...

5s - ticker fails to send value because channel is full

6s - ticker fails to send value because channel is full

6s - main goroutine receives buffered value

7s - ticker sends value

7s - main goroutine receives value

自动收报机在操场上以 6 秒的成绩赢得比赛。 在这里运行它。当睡眠时间减少 1µs 时,主 goroutine 赢得了比赛。在这里运行它。


当睡眠持续时间不是代码周期的倍数时,这很容易看出。以下是睡眠持续时间为 2.5 秒时发生的情况:


1s - ticker sends value

1s - main goroutine receives first value

1s - main goroutine starts sleep for 2.5s

2s - ticker sends value

3s - ticker fails to send value because channel is full

3.5s - main goroutine exits sleep and receives buffered value

4s - ticker sends value

4s - main goroutine receives value


查看完整回答
反对 回复 2022-11-08
?
慕的地8271018

TA贡献1796条经验 获得超4个赞

time.Ticker中,有一个chan Time. 在NewTicker(d Duration) *Ticker函数中,它创建tickers时间通道作为容量为1的缓冲通道。在函数中,如下所述。

// 给通道一个 1 元素的时间缓冲区。
// 如果客户在阅读时落后了,我们将滴答声
// 放在地板上,直到客户赶上。

在您的情况下,时间每秒都会滴答并写入频道。睡眠时,ticker 向通道写入一次,循环继续后立即被 select case 接收并打印。在另外 4 秒内,通道被阻塞并且滴答声被丢弃。立即循环继续它被畅通并接受更多的通道滴答声。

通过通道打印时间,您可以轻松了解行为。更新代码如下。

func main() {

    ticker := time.NewTicker(1 * time.Second)


    i := 0

    for {

        select {

        case t := <-ticker.C:

            log.Println(i, t)

            if i == 0 {

                time.Sleep(5 * time.Second) // simulate the blocking

            }

            i++

        }

    }

}

输出:


2022/02/20 08:47:58 0 2022-02-20 08:47:58.773375 +0530 +0530 m=+1.004241004

2022/02/20 08:48:03 1 2022-02-20 08:47:59.772787 +0530 +0530 m=+2.003666993

2022/02/20 08:48:03 2 2022-02-20 08:48:03.774272 +0530 +0530 m=+6.005207433 // printing time is same, but channel's received time has 4 sec difference.

2022/02/20 08:48:04 3 2022-02-20 08:48:04.774203 +0530 +0530 m=+7.005151715


查看完整回答
反对 回复 2022-11-08
  • 2 回答
  • 0 关注
  • 254 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号