我有一个OS X应用程序,它使用调度计时器每秒执行一次任务。简化代码如下所示:
timerQueue2 = dispatch_queue_create("com.mycompany.myappexample_t2", nil);
d_timer2 = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, timerQueue2);
dispatch_source_set_timer(d_timer2, //the timer
dispatch_walltime(NULL, 0), //the start time i guess
(1ull * NSEC_PER_SEC), //the interval
(1ull * NSEC_PER_SEC) / 1000ull); //the leeway
dispatch_source_set_event_handler(d_timer2, ^{
NSLog(@"%f\tT2 click", CACurrentMediaTime());
});
dispatch_resume(d_timer2);
它开始很好,每秒记录1次,但在使用任何其他应用程序10-15秒后,计时器将每10秒仅触发一次。我真的不知道是什么原因引起的。
以下是发生错误的摘录:
2014-08-25 18:39:10.967 JB4 App[9679:1003] 66156.545407 T2 click
2014-08-25 18:39:11.967 JB4 App[9679:1003] 66157.545426 T2 click
2014-08-25 18:39:12.967 JB4 App[9679:1003] 66158.545426 T2 click
2014-08-25 18:39:13.967 JB4 App[9679:1003] 66159.545425 T2 click
2014-08-25 18:39:14.967 JB4 App[9679:1003] 66160.545424 T2 click
2014-08-25 18:39:15.967 JB4 App[9679:1003] 66161.545425 T2 click
2014-08-25 18:39:16.967 JB4 App[9679:1003] 66162.545425 T2 click
2014-08-25 18:39:17.966 JB4 App[9679:1003] 66163.544404 T2 click
2014-08-25 18:39:18.967 JB4 App[9679:1003] 66164.545428 T2 click
2014-08-25 18:39:26.031 JB4 App[9679:a30b] 66171.610402 T2 click
2014-08-25 18:39:26.966 JB4 App[9679:a30b] 66172.545741 T2 click
2014-08-25 18:39:27.967 JB4 App[9679:a30b] 66173.545977 T2 click
2014-08-25 18:39:38.966 JB4 App[9679:a30f] 66184.545655 T2 click
2014-08-25 18:39:44.890 JB4 App[9679:a30f] 66190.469197 T2 click
2014-08-25 18:39:46.523 JB4 App[9679:a30f] 66192.102552 T2 click
2014-08-25 18:39:46.967 JB4 App[9679:a30f] 66192.547070 T2 click
2014-08-25 18:39:47.967 JB4 App[9679:a30f] 66193.547071 T2 click
2014-08-25 18:39:48.967 JB4 App[9679:a30f] 66194.546996 T2 click
2014-08-25 18:39:49.967 JB4 App[9679:a30f] 66195.547071 T2 click
2014-08-25 18:40:00.966 JB4 App[9679:a313] 66206.546097 T2 click
更新
似乎DISPATCH_TIMER_STRICT似乎对保持时间准确有所帮助,但现在我找到了别的东西。
除此之外,我还看到nanosleep有时会睡得太久。我想知道这些是否以某种方式相关。
//milliseconds is 100
double pre = CACurrentMediaTime();
struct timespec sleepSpec;
sleepSpec.tv_sec = 0;
sleepSpec.tv_nsec = milliseconds * 1000 * 1000; //ns
nanosleep(&sleepSpec, nil);
double post = CACurrentMediaTime();
if(post-pre > .15)
NSLog(@"Slept too long");
更新2:
这真的是等待确定时期的最好方式吗?
double pre = CACurrentMediaTime();
while(CACurrentMediaTime() - pre < .1) {
asm("NOP");
}
double post = CACurrentMediaTime();
if(post-pre > .15)
NSLog(@"Slept too long");
更新3:
我做了一个简单的测试项目,看看usleep是否真的像我怀疑的那样坏了。要么我完全误解了某些东西,要么这些睡眠功能毫无用处。
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
timerQueue2 = dispatch_queue_create("com.mycompany.myappexample_t2", nil);
d_timer2 = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, DISPATCH_TIMER_STRICT, timerQueue2);
dispatch_source_set_timer(d_timer2, //the timer
dispatch_walltime(NULL, 0), //the start time i guess
(1ull * NSEC_PER_SEC), //the interval
(1ull * NSEC_PER_SEC) / 1000ull); //the leeway
dispatch_source_set_event_handler(d_timer2, ^{
//[self timer2Method];
double time = CACurrentMediaTime();
if(lastTestTime > 0 && (time - lastTestTime) > 1.1) {
NSLog(@"Too much delay... %f", time-lastTestTime);
}
lastTestTime = time;
NSLog(@"%f\tClick", lastTestTime);
});
dispatch_resume(d_timer2);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
double lastTime = -1;
while(1) {
double thisTime = CACurrentMediaTime();
NSLog(@"%f", thisTime);
if(lastTime > 0 && (thisTime-lastTime)>1.1) {
//0.1 error range
NSLog(@"Slept too long: %f", thisTime - lastTime);
}
lastTime = thisTime;
usleep(1000000);
}
});
}
输出日志。正如你所看到的,usleep休息了11秒,而asm(“NOP”)继续正常运行。我知道睡眠不应该是“精确的”,但迟到10秒是不可接受的。
2014-08-25 20:32:14.435 TimingTest[11159:540b] 72940.352926 Click
2014-08-25 20:32:14.476 TimingTest[11159:3803] 72940.393701
2014-08-25 20:32:15.435 TimingTest[11159:540b] 72941.353001 Click
2014-08-25 20:32:15.477 TimingTest[11159:3803] 72941.395269
2014-08-25 20:32:16.435 TimingTest[11159:540b] 72942.353035 Click
2014-08-25 20:32:16.479 TimingTest[11159:3803] 72942.396839
2014-08-25 20:32:17.434 TimingTest[11159:540b] 72943.352364 Click
2014-08-25 20:32:17.479 TimingTest[11159:3803] 72943.397489
2014-08-25 20:32:18.435 TimingTest[11159:540b] 72944.353145 Click
2014-08-25 20:32:19.435 TimingTest[11159:540b] 72945.353202 Click
2014-08-25 20:32:20.435 TimingTest[11159:540b] 72946.353256 Click
2014-08-25 20:32:21.435 TimingTest[11159:540b] 72947.353306 Click
2014-08-25 20:32:22.435 TimingTest[11159:540b] 72948.353364 Click
2014-08-25 20:32:23.435 TimingTest[11159:540b] 72949.353420 Click
2014-08-25 20:32:24.435 TimingTest[11159:540b] 72950.353476 Click
2014-08-25 20:32:25.435 TimingTest[11159:540b] 72951.353530 Click
2014-08-25 20:32:26.435 TimingTest[11159:540b] 72952.353598 Click
2014-08-25 20:32:27.435 TimingTest[11159:540b] 72953.353637 Click
2014-08-25 20:32:28.435 TimingTest[11159:540b] 72954.353691 Click
2014-08-25 20:32:28.479 TimingTest[11159:3803] 72954.398038
2014-08-25 20:32:28.480 TimingTest[11159:3803] Slept too long: 11.000549
2014-08-25 20:32:29.435 TimingTest[11159:540b] 72955.353752 Click
答案 0 :(得分:1)
这是App Nap的结果。见WWDC 2013 video Improving Power Efficiency with App Nap。调度计时器源示例大约是视频的30分钟。
您可以通过提供DISPATCH_TIMER_STRICT
作为dispatch_source_create
的第三个参数来告知您的计时器不参与,尽管除非绝对需要(例如,与不能容忍计时器偏差的硬件接口),否则显然不鼓励这样做,如你失去了App Nap提供的节能。
答案 1 :(得分:1)
正如罗布在上面指出的那样,最大的问题是App Nap(这是一个偷偷摸摸的小东西)。使用DISPATCH_TIMER_STRICT为我的调度计时器工作,但不适用于一般的睡眠/等待。
这是我如何修复可怕的计时器准确度:
1)根据需要使用适当的标志打开串口时启动活动。
self.serialActivity = [[NSProcessInfo processInfo] beginActivityWithOptions:
(NSActivityLatencyCritical |
NSActivityIdleSystemSleepDisabled |
NSActivityAutomaticTerminationDisabled |
NSActivitySuddenTerminationDisabled |
NSActivityBackground)
reason:@"Serial Port IO"];
2)关闭串口时结束活动:
[[NSProcessInfo processInfo] endActivity:self.serialActivity];
现在我的计时器甚至可以使用usleep或sleepForTimeInterval。