POSIX定时器实现20ms周期线程实测时长偏短,是什么原因?
误差成因
- 计时逻辑错误
你当前的测量区间是「本次业务逻辑启动时间」到「本次wait_period返回时间」,这个值本质是「业务执行耗时 + 等待定时器触发的耗时」,本就不等于你设置的20ms周期。正确的周期测量应该是两次业务逻辑启动的时间差(即两次start变量采样的间隔),这个值才是你实际的任务调度周期。 - 定时器启动时机提前
你在make_periodic函数末尾调用timer_settime时,定时器就已经开始计时,而不是在第一次业务执行完成后才启动。调用make_periodic后,你还需要执行线程初始化printf、进入循环、采样start、执行业务逻辑,这一系列操作已经消耗了几百微秒不等的时间,等到你调用wait_period时,定时器已经走完了一小段,最终等待时长自然小于20ms,叠加后你的测量值就会稳定略小于20ms。 - IO操作的耗时波动
你在线程中混用了printf和std::cout两种IO操作,这类操作有内核缓冲、调度开销,本身耗时会有微秒级的波动,也会贡献小范围的测量误差。
现有实现的问题
- 基于POSIX信号的周期实现本身有额外开销:信号的触发、
sigwait的唤醒都有内核态到用户态的切换开销,优先级不高的线程还可能出现调度延迟,导致周期变长。 - 定时器创建后没有和任务执行的节奏对齐,第一次周期就存在固有偏差,长期运行下这个偏差不会累积,但首次调度的偏移是固定存在的。
优化建议
你可以改用clock_nanosleep的绝对时间模式实现周期线程,不需要依赖信号,逻辑更简单,精度更高:
static void *thread_1(void *arg) { const long period_ns = 20 * 1000 * 1000; // 20ms转纳秒 struct timespec next; // 以当前时间为基准初始化下次唤醒时间 clock_gettime(CLOCK_MONOTONIC, &next); while (1) { auto start = std::chrono::high_resolution_clock::now(); // 你的业务逻辑 printf("Hello\n"); thread_1_count++; // 计算下一次唤醒的绝对时间 next.tv_nsec += period_ns; if (next.tv_nsec >= 1000000000) { next.tv_sec += 1; next.tv_nsec -= 1000000000; } // 休眠到指定绝对时间,即使被信号中断也会自动重试 clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &next, NULL); } return NULL; }
内容的提问来源于stack exchange,提问作者Digget
相关产品推荐
相关产品推荐

