应使用 std::this_thread::sleep_until 实现固定周期,通过记录绝对时间点 next 并每次循环末尾 sleep_until(next) 后更新 next += period,配合 steady_clock 避免累积误差;需防范 do_work 超时、系统调度精度不足(Windows 需 timeBeginPeriod)、阻塞操作及测量实际抖动。

用 std::this_thread::sleep_until 控制固定周期,别用 sleep_for
固定频率循环的核心是“每次都在绝对时间点准时唤醒”,而不是“睡够 N 毫秒”。用 sleep_for 累积误差会越来越大——比如每次循环耗时 1.2ms,你设了 10ms 周期,50 次后就漂移了 10ms 以上。
正确做法是记录下一次执行的绝对时间点,每次循环末尾调用 sleep_until 补齐到该时刻:
auto next = std::chrono::steady_clock::now() + std::chrono::milliseconds(10);
while (running) {
do_work();
std::this_thread::sleep_until(next);
next += std::chrono::milliseconds(10);
}-
steady_clock是必须的,system_clock可能因系统时间调整跳变 - 把
next += ...放在sleep_until后面,避免因do_work()超时导致周期错乱 - 如果
do_work()耗时超过周期(比如要 15ms 但周期是 10ms),sleep_until会立即返回,下次循环不会补休——这是预期行为,表示已落后,需自行判断是否跳过、降频或告警
Windows 上用 WaitableTimer 更精准,尤其对 sub-millisecond 需求
Linux/macOS 下 sleep_until 在毫秒级足够稳;但 Windows 默认线程调度精度约 15ms,光靠 sleep_until 很难稳定跑 5ms 周期。这时得启用高精度定时器:
- 调用
timeBeginPeriod(1)提升系统最小计时粒度(需配对timeEndPeriod(1)) - 创建内核级
WaitableTimer,用SetWaitableTimer设置周期性触发 - 主线程用
WaitForSingleObject等待,醒来即执行,无 sleep 偏差
注意:提升系统计时精度会影响全局功耗和中断频率,仅在必要时启用,且务必成对调用 timeBeginPeriod/timeEndPeriod。
立即学习“C++免费学习笔记(深入)”;
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
避免在循环里做阻塞 I/O 或锁竞争
固定频率不等于“固定延迟”——只要 do_work() 中有不可控耗时操作,周期必然失守。常见陷阱包括:
- 调用
fread/recv等未设超时的阻塞读 - 持有 mutex 时间过长,尤其跨函数或含日志输出
- 动态内存分配(
new/malloc),在低内存或碎片化时可能卡住几毫秒 - 调用第三方库函数,文档没写清是否内部阻塞
对策不是加 try-catch,而是提前预估耗时、拆分任务、用非阻塞 I/O + 轮询,或把重负载移出主循环交给 worker thread 处理。
用 std::chrono::high_resolution_clock 测量实际周期抖动
理论周期 ≠ 实际周期。上线前必须实测抖动(jitter):记录每次循环起始时间,算相邻间隔的标准差。示例片段:
auto prev = std::chrono::high_resolution_clock::now();
while (running) {
auto now = std::chrono::high_resolution_clock::now();
auto delta_us = std::chrono::duration_cast<std::chrono::microseconds>(now - prev).count();
// 记录 delta_us 到数组或打点到文件
prev = now;
do_work();
std::this_thread::sleep_until(next);
next += period;
}重点看 99 分位抖动是否在容忍范围内(比如要求 ±100μs,结果发现 P99 是 ±800μs,就得查是不是 CPU 亲和性没绑、有没有其他进程抢核、或者 BIOS 关了 C-states)。
真正难的从来不是写个循环,而是让这个循环在真实机器上持续稳定地落在时间线上——所有优化都围绕这一点展开,而不是堆砌 fancy 的 timer 封装。

















