原文地址:https://developer.apple.com/library/content/technotes/tn2169/_index.html
iOS/OS X中的高精确度定时器
。。。
Timer工作原理
iOS和OS X中有很多API允许等待特定的一段时间。这些API可能是C或者Objective C写成的,他们有不同类型的参数,但是它们在内核中都以相同的代码结束。每一个定时器相关的api告诉内核他需要等待多长时间,例如从现在开始10秒钟。内核持续监控每一个线程,当一个定时器请求到来的时候,那个线程被标记为“我想要在10秒后执行”。
内核试图尽可能与中央处理器同步,所以如果没有其他任务执行时,它将让CPU睡10秒钟,然后唤醒并执行当前线程。
当然,那是一种最佳情况,在真实情景中并不是那么容易。有许多线程等待执行,而且有许多线程有定时器请求,内核需要管理全部定时器。当有数以万计的线程在仅有的cpu内核中执行的时候,定时器的不精确就能轻而易举的看出来。
高精确度的定时器如何工作
常规定时器和高精确度定时器的唯一不同在于线程的调度类。实时调度状态下的线程得到优先处理。当她们需要执行时它们会跑到队列的最前方。如果10秒钟后有其他线程也要执行时,实时线程通常优先执行。
如何使线程进入实时调度状态
The following code will move a pthread to the real time scheduling class
#include <mach/mach.h> |
#include <mach/mach_time.h> |
#include <pthread.h> |
|
void move_pthread_to_realtime_scheduling_class(pthread_t pthread) |
{ |
mach_timebase_info_data_t timebase_info; |
mach_timebase_info(&timebase_info); |
|
const uint64_t NANOS_PER_MSEC = 1000000ULL; |
double clock2abs = ((double)timebase_info.denom / (double)timebase_info.numer) * NANOS_PER_MSEC; |
|
thread_time_constraint_policy_data_t policy; |
policy.period = 0; |
policy.computation = (uint32_t)(5 * clock2abs); // 5 ms of work |
policy.constraint = (uint32_t)(10 * clock2abs); |
policy.preemptible = FALSE; |
|
int kr = thread_policy_set(pthread_mach_thread_np(pthread_self()), |
THREAD_TIME_CONSTRAINT_POLICY, |
(thread_policy_t)&policy, |
THREAD_TIME_CONSTRAINT_POLICY_COUNT); |
if (kr != KERN_SUCCESS) { |
mach_error("thread_policy_set:", kr); |
exit(1); |
} |
} |
我们应该使用哪个调度API?
正像上面提到的,所有的定时器函数都在内核的相同地点结束。但是其中一些更有效。我们推荐使用mach_wait_until(),这个函数在所有定时器API中有着最小开销。但是,如果你有mach_wait_until()无法满足的特定的需要时,例如你需要等待一个条件变量时,可以考虑使用合适的API。
This example code demonstrates using mach_wait_until() to wait exactly 10 seconds.
#include <mach/mach.h> |
#include <mach/mach_time.h> |
|
static const uint64_t NANOS_PER_USEC = 1000ULL; |
static const uint64_t NANOS_PER_MILLISEC = 1000ULL * NANOS_PER_USEC; |
static const uint64_t NANOS_PER_SEC = 1000ULL * NANOS_PER_MILLISEC; |
|
static mach_timebase_info_data_t timebase_info; |
|
static uint64_t abs_to_nanos(uint64_t abs) { |
return abs * timebase_info.numer / timebase_info.denom; |
} |
|
static uint64_t nanos_to_abs(uint64_t nanos) { |
return nanos * timebase_info.denom / timebase_info.numer; |
} |
|
void example_mach_wait_until(int argc, const char * argv[]) |
{ |
mach_timebase_info(&timebase_info); |
uint64_t time_to_wait = nanos_to_abs(10ULL * NANOS_PER_SEC); |
uint64_t now = mach_absolute_time(); |
mach_wait_until(now + time_to_wait); |
} |