原文地址: https://blog.cndenis.com/erlang/2014/09/internal_time_of_erlang.html
Erlang虚拟机可视为一个小型操作系统, 有自己的一套时钟系统, 与系统时间并不是完全一致的.
Erlang虚拟机时钟使用的是 erlang:now()
获取, 系统时间使用 os:timestamp()
获取.
格式是一样的三元组: {百万秒, 秒, 微秒}.
Erlang内部时钟的特点
为什么Erlang需要实现自己的虚拟机时钟呢? 因为系统时钟有时是不可靠的, 可能会因为联网自动对时或是管理员调时钟而发生突变或往回走, Erlang虚拟机时钟就是为了避免这些问题而设计的, 它有以下几个特点:
-
单向: 永远不会往回走
-
时间间隔接近于真实: 如果在两个时间读取虚拟机时钟, 读数为T1和T2, 那么T1-T2的值接近于两个时间点的真实时间间隔.
-
自动修正: 如果Erlang发现虚拟机内部时钟与系统时钟读数不一致, 它会自动缓慢地修正内部时钟, 使内部时钟与系统时钟一致.
第二点是说: 如果两次读取系统时钟, 发现时间相差1小时, 不一定是真实时间过了一小时, 可能是时钟被拨快了1小时, 实际上只过了几秒 但Erlang虚拟机时钟则可以保证, 读数相差1小时, 那么实际时间过了大约一小时, 误差不超过1%.
第三点的实现方法是, Erlang发现内部时间和系统时间差距"较大"的话, 会调节内部时钟走时速度来适应外部时钟. 比方说外部时钟突然快了1分钟, Erlang会用100分钟来"追赶"外部时钟, 这段时间内Erlang内部时钟走时会快1%.
Erlang的这套时钟修正机制虽然不是完美的, 但可以避免出现时间跳跃问题, 防止定时器过早或过晚触发.
Erlang内部时钟的应用
Erlang内部时钟在以下这个几地方使用:
erlang:now()
receive...after
timer
模块erlang:start_timer/3
和erlang:send_after/3
这就是说, 如果你用send_after
设置1小时后给某进程发消息,
想调系统时间让消息快点到是不可能的, 老老实实等1小时吧.
erlang:now() 的特点
- 单向
- 间隔真实
- 自动修正
- 唯一
前三点由Erlang的内部时钟机制保证, 不必多说. 最后一点是指, 即使在多进程下,
每次调用erlang:now()
都会获得不同的值.
因此erlang:now()
的返回值也可以拿来当唯一ID用, 不用担心重复.
不过这个机制也会造成在高频率(每秒大于1百万次)调用时, erlang:now()
的返回值有偏差.
因为它的返回值精度为1微秒, 1微秒调用2次的话, 也要值这个微秒数加2, 显得走快了.
当系统时间走时正常, 而且调用频率不高时, erlang:now()
显示的时间和系统时间是一致的.
关闭内部时钟自动修正
可以在启动erl的时候, 使用+c参数, 关闭内部时钟的自动修正. 这是一般用不上的功能.
erlang:now() vs os:timestamp()
os:timestamp()返回的是系统时钟, 调系统时间的时候就会立即改变. 这对于进行时间相关的逻辑的测试时是很有用的.
erlang:now()在单进程下稍快于os:timestamp(), 但多进程竞争调用会变慢, 可以参考我在stackoverflow上的提问.
参考: Erlang Run-Time System Application (ERTS) 第1.2节
原文地址: https://blog.cndenis.com/erlang/2014/09/internal_time_of_erlang.html