2024 年 Clojure 调查中分享您的想法!

欢迎!请参阅关于页面了解有关此操作的更多信息。

+8
core.async
编辑

clojure.core.async/timeout 使用 System/currentTimeMillis 来计算其截止时间(见这里这里)。虽然System/currentTimeMillis的文档字符串没有明确提及这一点,但它对于此目的显然不合适,因为它返回由操作系统报告的当前时间。由于该时间可能会任意更改(例如,通过NTP进行校正),截止时间的计算可能最终结果不一致。这种行为是故意的,例如请参阅这个票据,其中建议尽可能将System/currentTimeMillis转换为单调时钟。以下评论被拒绝

System.currentTimeMillis()需要返回自纪元以来的毫秒数,并应反映系统/墙钟时间的外部更改。CLOCK_MONOTONIC_COARSE不提供这一点,因此不能用于此目的。

java.util.concurrent.ScheduledExecutorService的文档字符串对此表述相当明确

所有计划方法都接受相对延迟和周期的参数,而不是绝对时间或日期。将表示为Date的绝对时间转换为所需形式是一个简单的问题。例如,要安排在未来某个日期,您可以这样做:schedule(task, date.getTime() - System.currentTimeMillis(), TimeUnit.MILLISECONDS)。但是请注意,由于网络时间同步协议、时钟漂移或其他因素,相对延迟的到期不一定与任务启用的当前日期相吻合。

由于我们在这里只关心差分,所以似乎最好改用System/nanoTime。不幸的是,它的文档字符串也没有清楚地表明它的属性。它没有提及这是一个单调时钟,而只是通过以下声明暗示了

此方法返回正在运行的Java虚拟机的高精度时间源当前值,以纳秒为单位。此方法只能用于测量已过时间,与任何其他系统或实时时间的概念无关。 [...] 此方法返回的值仅在计算同一Java虚拟机实例中获取的两个这样的值之间的差异时才有意义。

使用它的一个例子是OpenJDK的java.util.concurrent.ScheduledThreadPoolExecutor实现,其截止时间计算方法以及它的getDelay实现

此外——如果有什么作为依据的话——StackOverflow的各种帖子也讨论了这一点,并得出相同的结论,例如这个

尽管如此,对于实现计时阻塞、间期等待、超时等,仍应首选nanoTime(),而不是currentTimeMillis(),因为后者容易受到“时间倒流”现象的影响(例如,由于服务器时间校正),即currentTimeMillis()根本不适用于测量时间间隔。更多信息请参见此答案。

另请参阅有关core.cache的类似问题

1 答案

0

小的修正:在POSIX系统上,OpenJDK为System/nanoTime使用带有CLOCK_MONOTONICclock_gettime(请参阅OpenJDK在这个位置的使用clock_gettime),这会受到增量调整的影响,例如NTP所做的调整。然而,它确实保证永远不会倒退,并且不受连续跳变(例如,通过手动设置系统时间)的影响。这与那里使用的CLOCK_REALTIME(由System/currentTimeMillis使用)形成对比。有关详细信息,请参见man 2 clock_gettime

关于JDK-8006942的讨论对为什么即使对于System/nanoTime这种增量调整也可能是 desirable 也有一些见解。

以往由于NTP(以及其他)调整而产生过多种时间跳跃的错误,我的初始反应是进行任何类型对CLOCK_MONOTONIC的调整都是“疯狂”的,显然我们应该更喜欢未调整的CLOCK_MONOTONIC_RAW。然而,在仔细研究后,我同意Martin的观点,即施加的NTP扩展通常是可取的,以保持报告的流逝时间与实际流逝时间的一致性。这种扩展只是简单地调整了更新报告时间的方式,以考虑到运行速度比真实时间快或慢的硬件(几乎所有的硬件)。它不会引入可观察到的跳跃,对最终用户是看不到的。

...