应避免直接用 sleep_for 限制帧率,而需结合高精度计时、条件睡眠与忙等兜底;linux 可提权线程优先级配合 sleep_for 和微忙等,windows 则不适用;跨平台需实测权衡精度与开销。

用 std::chrono 控制每帧耗时,别碰 sleep_for 直接凑时间
帧率限制本质是让每帧「至少」耗时 1000ms / target_fps,不是“尽量睡够”。直接 sleep_for 会受系统调度干扰,实际帧率抖动大,尤其在 Windows 上可能睡过头或压根没睡够。
正确做法:记录上帧结束时间 → 计算本帧已用时 → 若不足目标帧间隔,则补足剩余时间(用 sleep_for),但必须配合忙等兜底(防止 sleep 精度丢失):
- 先用
std::this_thread::sleep_for睡到离目标时间还剩 0.1–0.3ms - 再用空循环(
while (now )精确卡点,避免 sleep 醒得太晚 - 忙等部分必须加
std::this_thread::yield(),否则吃满一个 CPU 核
std::chrono::high_resolution_clock 是唯一靠谱选择
别用 system_clock(受系统时间调整影响)或 steady_clock(某些平台精度仅 15ms)。high_resolution_clock 在主流编译器(MSVC、Clang、GCC)下基本等价于 steady_clock 且精度足够(通常 ≤ 1μs)。
常见错误:用 clock() 或 GetTickCount64() —— 前者是 C 风格且精度差,后者 Windows 专用且最小单位是 1ms,根本不够控 120FPS(误差达 ±0.8%)。
立即学习“C++免费学习笔记(深入)”;
- 统一用
auto start = std::chrono::high_resolution_clock::now() - 计算差值始终用
duration_cast<:chrono::microseconds></:chrono::microseconds>,避免隐式转换损失精度 - 不要存
time_point到 float/double,会丢精度
60FPS 和 144FPS 的阈值处理差异很大
目标帧率越高,对时间测量和补偿逻辑越敏感。60FPS 允许 ±1ms 误差,144FPS 要求 ±0.3ms 内稳定,否则肉眼可见卡顿。
关键区别在忙等阈值和 yield 频率:
- 60FPS(16.67ms/帧):忙等阈值设为 50μs,每 100 次循环
yield()一次 - 144FPS(6.94ms/帧):忙等阈值缩到 10–20μs,yield 改为每 20 次循环一次
- 若检测到连续 3 帧实际耗时 > 目标值 2×,说明渲染超载,应跳过补偿(避免恶性延迟累积)
Linux 下 pthread_setschedparam 可能比忙等更稳
在实时性要求高的 Linux 渲染服务中,单纯靠用户态忙等效果有限。此时可提权线程优先级,配合 sleep_for + 更宽松的忙等(比如只补最后 5μs)。
注意:这需要 root 权限或 CAP_SYS_NICE,且仅适用于专用渲染进程(桌面环境普通应用会被 cgroup 限频):
- 调用
pthread_setschedparam(pthread_self(), SCHED_FIFO, ¶m)设为实时策略 - 设置
param.sched_priority为 50–80(避免抢光内核线程) - 仍需保留 fallback 忙等逻辑,因为 SCHED_FIFO 不保证绝对准时唤醒
跨平台项目里,这部分必须条件编译,Windows 下完全不用考虑。
真正难的是平衡精度、CPU 占用和跨平台一致性——比如 macOS 的 mach_absolute_time 虽准,但换算开销比 high_resolution_clock 高一倍,得实测取舍。










