英文:
Comparing times in Goroutines
问题
我有一个用Go语言编写的测试:
func TestThings(t *testing.T) {
tCh := make(chan int64, 10000)
ctx, cx := context.WithTimeout(context.Background(), 20*time.Second)
defer cx()
wg := sync.WaitGroup{}
wg.Add(2)
go func(c context.Context) {
defer wg.Done()
defer close(tCh)
for {
select {
case <-c.Done():
return
default:
nt := time.Now()
tCh <- nt.UnixNano()
}
}
}(ctx)
go func(c context.Context) {
defer wg.Done()
for {
select {
case <-c.Done():
return
default:
v := <-tCh
nt := time.Now()
res := nt.UnixNano() - v
if res < 0 {
t.Errorf("got less than 0; diff: %d now: %d then: %d", res, nt.UnixNano(), v)
}
}
}
}(ctx)
wg.Wait()
}
运行这个测试有时会得到以下结果:
go test -v -run TestThings ./test
=== RUN TestThings
test.go:48: got less than 0; diff: -33686100 now: 1639183246323013700 then: 1639183246356699800
test.go:48: got less than 0; diff: -33535000 now: 1639183246323171300 then: 1639183246356706300
test.go:48: got less than 0; diff: -33490200 now: 1639183246323222600 then: 1639183246356712800
test.go:48: got less than 0; diff: -33488300 now: 1639183246323231000 then: 1639183246356719300
test.go:48: got less than 0; diff: -33502600 now: 1639183246323241000 then: 1639183246356743600
test.go:48: got less than 0; diff: -33551600 now: 1639183246323249100 then: 1639183246356800700
...
--- FAIL: TestThings (20.01s)
FAIL
FAIL test 20.022s
FAIL
为什么接收 Goroutine 中的时间有时会早于发送 Goroutine 中的时间?
我知道在计算机中,时钟通常是困难且不可靠的,但我期望由于这些 Goroutine(也许?)在同一个进程中,但肯定在同一台主机上,调用 time.Now()
应该返回递增的值。或者至少不是"更早"的值。
更新:
可能值得一提的另一件事:我在Macbook Pro上的VSCode devcontainer中运行这个测试。
英文:
I have a test written in Go:
func TestThings(t *testing.T) {
tCh := make(chan int64, 10000)
ctx, cx := context.WithTimeout(context.Background(), 20*time.Second)
defer cx()
wg := sync.WaitGroup{}
wg.Add(2)
go func(c context.Context) {
defer wg.Done()
defer close(tCh)
for {
select {
case <-c.Done():
return
default:
nt := time.Now()
tCh <- nt.UnixNano()
}
}
}(ctx)
go func(c context.Context) {
defer wg.Done()
for {
select {
case <-c.Done():
return
default:
v := <-tCh
nt := time.Now()
res := nt.UnixNano() - v
if res < 0 {
t.Errorf("got less than 0; diff: %d now: %d then: %d", res, nt.UnixNano(), v)
}
}
}
}(ctx)
wg.Wait()
}
Running this test (sometimes) ends with the result:
go test -v -run TestThings ./test
=== RUN TestThings
test.go:48: got less than 0; diff: -33686100 now: 1639183246323013700 then: 1639183246356699800
test.go:48: got less than 0; diff: -33535000 now: 1639183246323171300 then: 1639183246356706300
test.go:48: got less than 0; diff: -33490200 now: 1639183246323222600 then: 1639183246356712800
test.go:48: got less than 0; diff: -33488300 now: 1639183246323231000 then: 1639183246356719300
test.go:48: got less than 0; diff: -33502600 now: 1639183246323241000 then: 1639183246356743600
test.go:48: got less than 0; diff: -33551600 now: 1639183246323249100 then: 1639183246356800700
...
--- FAIL: TestThings (20.01s)
FAIL
FAIL test 20.022s
FAIL
Why are the times in the receiving Goroutine sometimes before the times in the sending Goroutine?
I understand that clocks are generally hard and unreliable when it comes to computers, but I would expect since these Goroutines are (maybe?) in the same process, but definitely on the same host, that calls to time.Now()
would return ever increasing values. Or at least not "earlier" ones.
Update:
Something else possibly worth mentioning: I'm running this inside a VSCode devcontainer on a Macbook Pro.
答案1
得分: 0
根据原始问题中@Peter的评论,引用了time包文档,time.Time
的序列化设计上会丢失任何单调信息:
因为单调时钟读数在当前进程之外没有意义,所以由
t.GobEncode
、t.MarshalBinary
、t.MarshalJSON
和t.MarshalText
生成的序列化形式会省略单调时钟读数,而t.Format
不提供其格式。同样,构造函数time.Date
、time.Parse
、time.ParseInLocation
和time.Unix
,以及解组函数t.GobDecode
、t.UnmarshalBinary
、t.UnmarshalJSON
和t.UnmarshalText
总是创建没有单调时钟读数的时间。
将原始问题中的代码片段更改为在Goroutine之间传递time.Time
值没有问题。
英文:
As commented by @Peter on the original question referencing the time package docs, serialization of time.Time
loses any monotonic information by design:
> Because the monotonic clock reading has no meaning outside the current
> process, the serialized forms generated by t.GobEncode
, t.MarshalBinary
,
> t.MarshalJSON
, and t.MarshalText
omit the monotonic clock reading, and
> t.Format
provides no format for it. Similarly, the constructors time.Date
,
> time.Parse
, time.ParseInLocation
, and time.Unix
, as well as the unmarshalers
> t.GobDecode
, t.UnmarshalBinary
. t.UnmarshalJSON
, and t.UnmarshalText
always
> create times with no monotonic clock reading.
Changing the code snippet from the original question to pass time.Time
values between the Goroutines works without issue.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论