为什么 benbjohnson/clock 模拟计时器在 goroutine 中声明时不执行?
Why does benbjohnson/clock mock timer not execute when declared inside a goroutine?
此代码如我所料工作
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 外部声明计时器还是在 goroutine 内部声明计时器。 mockClock
Timer()
方法有一个指针接收器和 returns 一个指针。我无法解释为什么第一个有效而第二个无效。
包 benbjohnson/clock
提供 mock 时间工具。特别是他们的文档状态:
Timers and Tickers are also controlled by this same mock clock. They will only execute when the clock is moved forward
因此当您调用 mockClock.Add
时,它将顺序 执行 timers/tickers。该库还添加了连续的 1 毫秒睡眠,以人为地屈服于其他 goroutines。
当 timer/ticker 在 goroutine 外部声明时,即在调用 mockClock.Add
之前,到 mockClock.Add
被调用时,模拟时间确实有一些东西要执行。在程序退出之前,库的内部睡眠足以让子 goroutine 在自动收报机上接收并打印“完成”。
当代码在 goroutine 中声明时,到 mockClock.Add
被调用时,模拟时间没有代码可执行,Add
基本上什么都不做。内部睡眠确实给了子 goroutine 到 运行 的机会,但是现在在自动收报机上接收只是阻塞; 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()
在 调用 mock.Add
之前屈服于子 goroutine 。这个程序的顺序基本上是:
clock.NewMock()
count := 0
- 生成子 goroutine
runtime.Gosched()
,让步给子 goroutine
ticker := mock.Ticker(1 * time.Second)
- 阻塞
<-ticker.C
(模拟时钟尚未向前移动)
- 恢复主要
mock.Add
,将时钟向前移动并再次让步给子 goroutine
for
循环 <-ticker.C
- 打印 10
- 退出
按照相同的逻辑,如果您将 runtime.Gosched()
添加到第二个代码段,它将按预期工作,就像存储库的示例一样。游乐场:https://go.dev/play/p/ZitEdtx9GdL
但是,请勿在生产代码中依赖 runtime.Gosched()
,甚至在测试代码中也不要依赖,除非您非常确定自己在做什么。
最后,请记住 time.Sleep(1)
睡眠一 nano 秒。
此代码如我所料工作
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 外部声明计时器还是在 goroutine 内部声明计时器。 mockClock
Timer()
方法有一个指针接收器和 returns 一个指针。我无法解释为什么第一个有效而第二个无效。
包 benbjohnson/clock
提供 mock 时间工具。特别是他们的文档状态:
Timers and Tickers are also controlled by this same mock clock. They will only execute when the clock is moved forward
因此当您调用 mockClock.Add
时,它将顺序 执行 timers/tickers。该库还添加了连续的 1 毫秒睡眠,以人为地屈服于其他 goroutines。
当 timer/ticker 在 goroutine 外部声明时,即在调用 mockClock.Add
之前,到 mockClock.Add
被调用时,模拟时间确实有一些东西要执行。在程序退出之前,库的内部睡眠足以让子 goroutine 在自动收报机上接收并打印“完成”。
当代码在 goroutine 中声明时,到 mockClock.Add
被调用时,模拟时间没有代码可执行,Add
基本上什么都不做。内部睡眠确实给了子 goroutine 到 运行 的机会,但是现在在自动收报机上接收只是阻塞; 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()
在 调用 mock.Add
之前屈服于子 goroutine 。这个程序的顺序基本上是:
clock.NewMock()
count := 0
- 生成子 goroutine
runtime.Gosched()
,让步给子 goroutineticker := mock.Ticker(1 * time.Second)
- 阻塞
<-ticker.C
(模拟时钟尚未向前移动) - 恢复主要
mock.Add
,将时钟向前移动并再次让步给子 goroutinefor
循环<-ticker.C
- 打印 10
- 退出
按照相同的逻辑,如果您将 runtime.Gosched()
添加到第二个代码段,它将按预期工作,就像存储库的示例一样。游乐场:https://go.dev/play/p/ZitEdtx9GdL
但是,请勿在生产代码中依赖 runtime.Gosched()
,甚至在测试代码中也不要依赖,除非您非常确定自己在做什么。
最后,请记住 time.Sleep(1)
睡眠一 nano 秒。