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

为什么在 goroutine 中声明时 benbjohnson/clock 模拟计时器不执行?

为什么在 goroutine 中声明时 benbjohnson/clock 模拟计时器不执行?

Go
RISEBY 2022-11-28 17:03:52
此代码按我预期的方式工作import (    "fmt"    "time"    "github.com/benbjohnson/clock")func main() {    mockClock := clock.NewMock()    timer := mockClock.Timer(time.Duration(2) * time.Second)    go func() {        <-timer.C        fmt.Println("Done")    }()    mockClock.Add(time.Duration(10) * time.Second)    time.Sleep(1)}它按我的预期打印“完成”。而这个功能不import (    "fmt"    "time"    "github.com/benbjohnson/clock")func main() {    mockClock := clock.NewMock()    go func() {        timer := mockClock.Timer(time.Duration(2) * time.Second)        <-timer.C        fmt.Println("Done")    }()    mockClock.Add(time.Duration(10) * time.Second)    time.Sleep(1)}这里唯一的区别是我在 goroutine 外部和内部声明定时器。该mockClock Timer()方法有一个指针接收器并返回一个指针。我无法解释为什么第一个有效而第二个无效。
查看完整描述

1 回答

?
弑天下

TA贡献1818条经验 获得超7个赞

该软件包benbjohnson/clock提供模拟时间设施。他们的文档特别指出:


计时器和 Tickers 也由同一个模拟时钟控制。它们只会在时钟向前移动时执行


所以当你调用时mockClock.Add,它会依次执行定时器/代码。该库还添加了连续的 1 毫秒睡眠,以人为地屈服于其他 goroutines。


当计时器/自动收报机在 goroutine 外部声明时,即在调用之前mockClock.Add,到mockClock.Add被调用时,模拟时间确实有一些东西要执行。库的内部睡眠足以让子 goroutine 在程序退出之前在自动收报机上接收并打印“完成”。


当 ticker 在 goroutine 中声明时,到 timemockClock.Add被调用时,模拟时间没有要执行的 ticker,Add基本上什么都不做。内部睡眠确实给了子 goroutine 运行的机会,但是接收到 ticker 现在只会阻塞;main 然后恢复并退出。


您还可以查看存储库自述文件中的代码示例:


mock := clock.NewMock()

count := 0


// Kick off a timer to increment every 1 mock second.

go func() {

    ticker := mock.Ticker(1 * time.Second)

    for {

        <-ticker.C

        count++

    }

}()

runtime.Gosched()


// Move the clock forward 10 seconds.

mock.Add(10 * time.Second)


// This prints 10.

fmt.Println(count)

这用于在调用之前runtime.Gosched()屈服于子 goroutine 。这个程序的顺序基本上是:mock.Add

  • clock.NewMock()

  • count := 0

  • 生成子 goroutine

  • runtime.Gosched(), 让步给子 goroutine

  • ticker := mock.Ticker(1 * time.Second)

  • 阻塞<-ticker.C(模拟时钟还没有向前移动)

  • 恢复主要

  • mock.Add, 它将时钟向前移动并再次让步给子 goroutine

  • for循环<-ticker.C

  • 打印 10

  • 出口

按照相同的逻辑,如果您将 a 添加runtime.Gosched()到第二个代码段,它将按预期工作,就像存储库的示例一样。游乐场:https ://go.dev/play/p/ZitEdtx9GdL

但是,不要依赖runtime.Gosched()生产代码,甚至可能不要依赖测试代码,除非您非常确定自己在做什么。


最后,请记住time.Sleep(1)休眠一纳秒


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

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信