这段代码按照我的预期工作

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)
}

It prints "Done" as I expect.

然而,该函数没有

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外部和内部声明计时器.mockClockTimer()方法有一个指针接收器并返回一个指针.我无法解释为什么第一个有效,第二个无效.

推荐答案

套餐benbjohnson/clock提供mock个计时设施.他们的文件特别指出:

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

所以当你呼叫mockClock.Add时,它将执行计时器/计时器.该库还添加了连续1毫秒的睡眠,以人为地向其他goroutines屈服.

当计时器/计时器在goroutine之外声明时,即在调用mockClock.Add之前,在调用mockClock.Add时,模拟时间确实要执行.图书馆的内部睡眠足以让子元素goroutine在程序退出前在股票行情器上接收并打印"完成".

当在goroutine中声明ticker时,在调用mockClock.Add时,模拟时间没有要执行的ticker,而Add基本上什么都不做.内部睡眠确实给了子元素goroutine一个 run 的机会,但现在接收到的信息只是块;然后主电源恢复并退出.

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

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 before呼叫mock.Add.这个项目的顺序基本上是:

  • clock.NewMock()
  • count := 0
  • 产卵子goroutine
  • runtime.Gosched()岁,屈服于子元素戈鲁廷
  • ticker := mock.Ticker(1 * time.Second)
  • <-ticker.C挡(模拟时钟还没有向前移动)
  • 恢复主
  • mock.Add,它将时钟向前移动,并再次让位给子元素goroutine
  • for圈加<-ticker.C
  • 打印10
  • 出口

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

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


最后,请记住time.Sleep(1)人睡一秒钟.

Go相关问答推荐

golang父进程的副本无法进行https/tls调用并获得tls:未能验证证书""

JetBrains Goland,禁用突出显示测试文件

通过渠道和goroutines增值1000倍

如何使用 AWS sdk 在 Go 中正确解组 PartiQL 查询的结果?

Kafka golang 生产者在错误后更改分区计数

生成一个 CSV/Excel,在 Golang 中该列的下拉选项中指定值

Go Template if 条件

获取 nil 指针类型的 reflect.Value

如何使用管理员权限启动 powershell 进程并重定向标准输入 (os.exec)

无法将 graphql-ws 连接到 gqlgen

golang 中的可变参数函数

Golang 通过接口反映/迭代{}

在 Golang 模板中计算时间/持续时间

处理程序后访问 HTTP 请求上下文

Go 并发、goroutine 同步和关闭通道

使用 Golang SQL 驱动程序连接到snowflake

在 Go 中将十六进制转换为带符号的 Int

Go模板中的浮点除法

在 VSCode 中使用命令行参数调试 Go 测试

Go 1.18 泛型如何使用接口定义新的类型参数