昨天群里有人问了一个问题
golang defer 函数参数是什么时候求值的哈, 我这边试了下输出有时候两者是一样的,有时候两者相差 1000ms
这里面的 defer 的参数计算问题可以在这篇文章中找到
本文主要讨论 time.Now
函数的精度问题。
先看一下下面这段代码
package main
import (
"fmt"
"time"
)
func test() {
for i := 0; i <= 1000; i++ {
start := time.Now() // 1
defer fmt.Print(time.Now().Nanosecond()-start.Nanosecond(), ",") // 2
time.Sleep(1000) // 3
}
fmt.Println()
}
func test2() {
s := time.Now() //4
N := 1000
for i := 0; i <= N; i++ {
time.Now() // 5
}
fmt.Println()
fmt.Println((time.Now().Nanosecond() - s.Nanosecond()) / (N + 1)) // 6
}
func main() {
test()
test2()
}
test
函数就是截图里面那位朋友的代码。
首先,line 2
肯定是先计算 time.Now()
,然后再将参数入栈的,现在的问题是:这样的代码的结果应该是固定的,为什么有时候返回 0ns
,有时候返回 1000ns
?
我看到这段代码后,想到的第一个原因就是 time.Now
这个函数的执行时间导致的上面的问题,但是仔细一想:如果是这样的话,name 应该每次相差的结果(line 2
print 的)应该差不多呀,为什么大部分是 0,少部分是 1000 呢?而且为什么是 1000,不是 2000 呢?
然后我就翻了一下 go
的 time.Now
的源码:
//go:linkname time_now time.now
func time_now() (sec int64, nsec int32, mono int64) {
sec, nsec = walltime()
return sec, nsec, nanotime()
}
//go:nosplit
//go:cgo_unsafe_args
func walltime() (int64, int32) {
var t timeval
libcCall(unsafe.Pointer(funcPC(walltime_trampoline)), unsafe.Pointer(&t))
return int64(t.tv_sec), 1000 * t.tv_usec // line 7
}
[捂脸]根据 line 7
,在 darwin(mac)
系统上,go 的 time.Now
精度就是 1000ns。
所以在上面 line 2
的代码中,每个 time.Now
都是 80 - 150 ns 的时间,然后 print 0,到 1000 ns 过去后,就 print 一个 1000 ns
将上面的代码交叉编译并在 linux 系统上执行,结果是:
嗯...是正常的了。