使用std::chrono计算DeltaTime时循环计时异常的技术咨询
嘿,我来帮你拆解这个奇怪的现象!你遇到的问题其实不是std::chrono本身的bug,而是忙等循环导致的时间测量精度问题,具体原因和解决方法如下:
问题根源:循环执行速度超过时钟分辨率,导致DeltaTime为0
当你去掉printf后,这个循环变成了一个几乎无耗时操作的忙等循环——CPU会以极快的速度重复执行now = chrono_clock::now();和lastUpdate = now;这两行代码。
std::chrono::steady_clock虽然精度很高,但它的时间戳是离散的(比如大多数系统上,最小时间单位是纳秒级,部分系统是微秒级)。如果两次调用now()的时间间隔小于这个最小单位,那么now和lastUpdate会是完全相同的时间点,导致delta_duration = now - lastUpdate的结果为0:
- 此时
deltaTime = duration_cast<microseconds>(delta_duration).count()会得到0,deltaTime_sec也会是0 - 你的
timer变量根本不会累加,循环会一直空转,只有当时钟的时间戳自然跳变时,才会产生非零的DeltaTime,这就导致timer需要极长的时间才能累加到1.0f
而当你保留printf时,每次循环的IO操作(控制台打印)会消耗几微秒甚至更长的时间,这个耗时远远超过了时钟的最小分辨率,所以每次now和lastUpdate之间都会有明显的时间差,deltaTime_sec始终为正,timer就能正常累加,1秒左右就达到目标值。
额外的代码小错误
顺便提一句,你代码里的这行:
auto deltaTime_sec_2 = delta_duration.count();
是有问题的——delta_duration.count()返回的是时钟周期的原始计数(比如steady_clock通常是纳秒数),不是秒数,这行代码的结果并不是你预期的“以秒为单位的DeltaTime”,正确的写法应该是:
auto deltaTime_sec_2 = std::chrono::duration_cast<std::chrono::duration<float>>(delta_duration).count();
解决方案
针对这个问题,有几个可行的修复方案:
方案1:避免忙等,加入最小休眠
如果你的场景不需要极致的帧率,可以在循环里加入一个微小的休眠,保证每次循环至少消耗一点时间,让DeltaTime始终非零:
while (true) { now = chrono_clock::now(); // ... 你的DeltaTime计算逻辑 ... lastUpdate = now; frame_count++; // 加入1微秒的休眠,避免忙等 std::this_thread::sleep_for(std::chrono::microseconds(1)); if (timer < duration) continue; // ... 退出逻辑 ... }
方案2:直接判断总耗时,放弃DeltaTime累加
其实你根本不需要累加timer,直接计算从开始到当前的总耗时是否达到目标即可,这样完全避开了DeltaTime为0的问题,精度也更高:
while (true) { now = chrono_clock::now(); auto sinceStartDuration = now - startTime; // 直接将总耗时转为float类型的秒数 auto timeSinceStart_sec = std::chrono::duration_cast<std::chrono::duration<float>>(sinceStartDuration).count(); // ... 你的打印逻辑 ... frame_count++; // 直接判断总耗时是否小于目标duration if (timeSinceStart_sec < duration) continue; printf(">>>>>End with timeSinceStart:[%f]", timeSinceStart_sec); break; }
方案3:使用更高精度的时间转换
如果你必须保留DeltaTime累加的逻辑,可以把DeltaTime转换到更高精度的单位(比如纳秒),这样即使时间间隔极小,也能得到非零的数值:
// 改用纳秒计算DeltaTime auto deltaTime_ns = std::chrono::duration_cast<std::chrono::nanoseconds>(delta_duration).count(); auto deltaTime_sec = deltaTime_ns * 1e-9f; // 纳秒转秒 timer += deltaTime_sec;
这样即使循环跑得极快,每次DeltaTime至少是1纳秒对应的秒数,timer会缓慢但持续地累加,不会卡住。
内容来源于stack exchange

