只需使用Mach Time即可。
它是公共API,适用于macOS、iOS和tvOS,并且可以在沙盒内使用。
Mach Time返回一个抽象的时间单位,我通常称之为“时钟滴答声”。时钟滴答声的长度是系统特定的,取决于CPU。在当前的Intel系统上,时钟滴答声实际上正好是一纳秒,但您不能依赖于这一点(对于ARM可能不同,对于PowerPC CPU肯定不同)。系统还可以告诉您将时钟滴答声转换为纳秒和将纳秒转换为时钟滴答声所需的转换因子(此因子是静态的,在运行时不会更改)。当您的系统启动时,时钟从0
开始,然后随着每个时钟滴答声单调递增,因此您也可以使用Mach Time获取系统的正常运行时间(当然,正常运行时间是单调递增的!)。
下面是一些代码:
#include <stdio.h>
#include <inttypes.h>
#include <mach/mach_time.h>
int main ( ) {
uint64_t clockTicksSinceSystemBoot = mach_absolute_time();
printf("Clock ticks since system boot: %"PRIu64"\n",
clockTicksSinceSystemBoot
);
static mach_timebase_info_data_t timebase;
mach_timebase_info(&timebase);
double clockTicksToNanosecons = (double)timebase.numer / timebase.denom;
uint64_t systemUptimeNanoseconds = (uint64_t)(
clockTicksToNanosecons * clockTicksSinceSystemBoot
);
uint64_t systemUptimeSeconds = systemUptimeNanoseconds / (1000 * 1000 * 1000);
printf("System uptime: %"PRIu64" seconds\n", systemUptimeSeconds);
}
你还可以将线程休眠直到达到特定的 Mach 时间。以下是相关代码:
uint64_t machTimeNow = mach_absolute_time();
uint64_t clockTicksToSleep = (uint64_t)(750 / clockTicksToNanosecons);
uint64_t machTimeIn750ns = machTimeNow + clockTicksToSleep;
mach_wait_until(machTimeIn750ns);
作为 Mach Time 与任何实际时间无关,因此您可以随意调整系统的日期和时间设置,这不会对 Mach Time 产生任何影响。
但是有一个特殊的考虑因素可能使 Mach Time 不适用于某些用例:当您的系统处于睡眠状态时,
CPU 时钟不运行!因此,如果您让一个线程等待 5 分钟,而在 1 分钟后系统进入睡眠状态并保持 30 分钟,那么该线程仍将在系统唤醒后等待另外 4 分钟,因为这 30 分钟的睡眠时间不计算在内!CPU 时钟在此期间也在休息。然而,在其他情况下,这正是您想要发生的事情。
Mach Time 还是一种非常精确的测量时间消耗的方法。以下是一些展示该任务的代码:
// Measure time
uint64_t machTimeBegin = mach_absolute_time();
sleep(1);
uint64_t machTimeEnd = mach_absolute_time();
uint64_t machTimePassed = machTimeEnd - machTimeBegin;
uint64_t timePassedNS = (uint64_t)(
machTimePassed * clockTicksToNanosecons
);
printf("Thread slept for: %"PRIu64" ns\n", timePassedNS);
你会发现线程并不会准确地休眠一秒钟,这是因为将线程置于休眠状态需要一些时间,唤醒线程再次运行也需要时间,即使线程被唤醒后,如果所有核心都已经在运行一个线程,那么它也不会立即获得CPU时间。
更新(2018-09-26)
自macOS 10.12(Sierra)以来,也存在mach_continuous_time。 mach_continuous_time和mach_absolute_time之间唯一的区别在于连续时间在系统休眠时也会增加。因此,如果这是迄今为止的问题和不使用Mach Time的原因,则10.12及以上版本提供了解决此问题的解决方案。使用方法与上述描述完全相同。
从 macOS 10.9(Mavericks)开始,有一个 mach_approximate_time
,在 10.12 中还有一个 mach_continuous_approximate_time
。这两个与 mach_absolute_time
和 mach_continuous_time
相同,唯一的区别是它们更快但不太准确。标准函数需要调用内核,因为内核负责 Mach 时间。这样的调用有点昂贵,特别是在已经修复了 Meltdown 的系统上。近似版本不必总是调用内核。它们使用用户空间中的时钟,只有在必要时才与内核时钟同步,以防止其运行过于失调,然而小偏差总是可能的,因此它只是“近似”Mach时间。
CLOCK_MONOTONIC
并不能保证值不会漂移,也不能保证线程亲和性没有问题。 - zneakmach_absolute_time
确实使用了rtdsc,具体请参考源代码. - cobbalclock_set_time
系统性地返回KERN_FAILURE
。至于哪个是合适的,SYSTEM_CLOCK
"单调且均匀地前进"。这听起来很像CLOCK_MONOTONIC
。 - zneak