Go中使用单调时钟获得准确的时间间隔问题

作者:幽鬼 时间:2024-05-22 10:14:19 

墙上时钟与单调时钟

墙上时钟

墙上时钟也称为墙上时间。大多是1970年1月1日(UTC)以来的秒数和毫秒数。

墙上时间可以和NTP(Network Time Protocal,网络时间协议)同步,但是如果本地时钟远远快于NTP服务器,则强制重置之后会跳到先前某个时间点。(这里不是很确定,猜测是如果时间差的不多,则调整石英晶体振荡器的频率,慢慢一致。如果差很多,则强行一致)

单调时钟

机器大多有自己的石英晶体振荡器,并将其作为计时器。单调时钟的绝对值没有任何意义,根据操作系统和语言的不同,单调时钟可能在程序开始时设为0、或在计算机启动后设为0等等。但是通过比较同一台计算机上两次单调时钟的差,可以获得相对准确的时间间隔。

Time的结构

type Time struct {
   // wall and ext encode the wall time seconds, wall time nanoseconds,
   // and optional monotonic clock reading in nanoseconds.
   //
   // From high to low bit position, wall encodes a 1-bit flag (hasMonotonic),
   // a 33-bit seconds field, and a 30-bit wall time nanoseconds field.
   // The nanoseconds field is in the range [0, 999999999].
   // If the hasMonotonic bit is 0, then the 33-bit field must be zero
   // and the full signed 64-bit wall seconds since Jan 1 year 1 is stored in ext.
   // If the hasMonotonic bit is 1, then the 33-bit field holds a 33-bit
   // unsigned wall seconds since Jan 1 year 1885, and ext holds a
   // signed 64-bit monotonic clock reading, nanoseconds since process start.
   wall uint64
   ext  int64
   ...
}

wall和ext共同记录了时间,但是分为两种情况,一种是没有记录单调时钟(比如是通过字符串解析得到的时间),另一种是记录了单调时钟(比如通过Now)。

wall的第一位是一个标记位

如果为1,则表示记录了单调时钟。则wall的2-34(闭区间)位记录了从1885-1-1到现在的秒数,最后30位记录了纳秒数。而ext记录了从程序开始运行到现在经过的单调时钟数。

如果为0,则表示没有记录单调时钟。则wall的2-34(闭区间)位全部为0(那最后30位是啥?)。而ext记录了从1-1-1到现在经过的秒数。

Since的实现

Go中使用单调时钟获得准确的时间间隔问题

这里比较关键的代码是第914行的 runtimeNano() - startNano 。 startNano 的含义还是直接上代码比较明了。

var startNano = 0
func init(){
    startNano = runtimeNano()
}

runtimeNano() 是调用了汇编,获取了操作系统当前的单调时钟。前面说过,单调时钟的绝对值没有什么意义。因此这里将两个时间相减,得到了从程序开始到现在的单调时钟。

然后看一下Sub

func (t Time) Sub(u Time) Duration {
   if t.wall&u.wall&hasMonotonic != 0 {
      te := t.ext
      ue := u.ext
      d := Duration(te - ue)
      if d < 0 && te > ue {
         return maxDuration // t - u is positive out of range
      }
      if d > 0 && te < ue {
         return minDuration // t - u is negative out of range
      }
      return d
   }
   d := Duration(t.sec()-u.sec())*Second + Duration(t.nsec()-u.nsec())
   // Check for overflow or underflow.
   switch {
   case u.Add(d).Equal(t):
      return d // d is correct
   case t.Before(u):
      return minDuration // t - u is negative out of range
   default:
      return maxDuration // t - u is positive out of range
   }
}

这里我们只需要关注2-13行即可。除去了范围检查,这里的主要逻辑就是两个Time的ext相减。而ext又都代表了单调时钟,所以最后返回的是单调时钟的差值。

小结

在分布式系统中,我们经常需要判断时间间隔来检测心跳。而墙上时钟与NTP的组合可能会带来时间的前后跳跃与闪烁,所以使用单调时钟更加安全和保险。

在go语言中,没有直接调用调用时钟的函数。可以通过 time.Now() 获得带单调时钟的 Time 结构体,并通过Since和Until获得相对准确的时间间隔。

参考资料

  • go time分析:https://zhuanlan.zhihu.com/p/93299132

  • 一个commit:https://link.zhihu.com/?target=https%3A//github.com/golang/go/commit/fc3f8d43f1b7da3ee3fb9a5181f2a86841620273

  • go1.14.2 源码

  • 数据密集型应用系统设计(书)

来源:https://mp.weixin.qq.com/s?__biz=MzkyMTI5MTgzNg==&mid=2247484818&idx=1&sn=c965af56ed87d17b3b8b19ab503a1186&utm_source=tuicool&utm_medium=referral

标签:go,单调时钟,时间间隔
0
投稿

猜你喜欢

  • 浅谈Python类的__getitem__和__setitem__特殊方法

    2022-05-05 00:21:16
  • 浅析node命令行交互原理

    2024-05-11 10:14:36
  • Python3 Random模块代码详解

    2023-04-11 01:36:20
  • Mysql如何通过binlog日志恢复数据详解

    2024-01-16 16:53:57
  • django实现图片上传数据库并显示

    2024-01-13 08:52:24
  • 分享个asp文件缓存代码,使程序从缓存读数据

    2011-03-09 19:47:00
  • JavaScript实现iframe自动高度调整和不同主域名跨域

    2024-04-16 09:47:33
  • Python 登录网站详解及实例

    2022-05-31 00:47:57
  • Python3 安装PyQt5及exe打包图文教程

    2021-09-24 12:43:56
  • Pytorch:dtype不一致问题(expected dtype Double but got dtype Float)

    2023-07-05 21:57:33
  • 详解python OpenCV学习笔记之直方图均衡化

    2022-12-29 20:37:22
  • 使用Python防止SQL注入攻击的实现示例

    2022-02-09 05:28:24
  • Spring数据库事务的实现机制讲解

    2024-01-19 11:32:10
  • ERROR CODE: 1175 YOU ARE USING SAFE UPDATE MODE AN

    2024-01-17 21:57:50
  • python批量修改图片后缀的方法(png到jpg)

    2022-01-19 19:09:24
  • 关于捕获用户何时点击window.onbeforeunload的取消事件

    2024-04-22 22:45:07
  • 利用PHP自动生成印有用户信息的名片

    2023-09-10 22:30:56
  • python3.4爬虫demo

    2023-10-24 21:46:24
  • Python 16进制与中文相互转换的实现方法

    2023-10-03 15:58:35
  • Python使用time模块实现指定时间触发器示例

    2022-05-13 02:57:59
  • asp之家 网络编程 m.aspxhome.com