应使用 std::chrono::steady_clock 进行超时重试判断,因其单调递增、不受系统时间调整影响;避免混用 clock 类型,不依赖 high_resolution_clock;推荐预计算 deadline 而非每次计算耗时。

超时重试的核心判断:用 std::chrono::steady_clock 而不是 system_clock
系统时间可能被手动调整或 NTP 同步回拨,system_clock 会跳变,导致超时计算错误——比如本该 5 秒超时,却因时间回拨多等了 30 秒。而 steady_clock 是单调递增的,只随物理时间流逝推进,是唯一适合超时控制的时钟。
实操建议:
- 所有超时逻辑一律用
std::chrono::steady_clock::now()获取起始和当前时间点 - 避免混用不同 clock 类型(如把
steady_clock::time_point强转成system_clock::time_point),编译可能通过但语义错误 - 不要依赖
high_resolution_clock:它在某些平台(如 macOS)只是system_clock的别名,不保证单调
重试循环中如何正确检查是否超时
常见错误是每次循环都调用一次 steady_clock::now() 并与初始时间做差,再跟总超时比较——这本身没错,但容易忽略「最后一次重试失败后是否还要再等」这个边界。
更稳妥的做法是预计算截止时间点,每次循环只比对当前时间是否已过期:
立即学习“C++免费学习笔记(深入)”;
auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(10);
int attempt = 0;
while (std::chrono::steady_clock::now() < deadline && attempt < 3) {
if (do_something()) break; // 成功则退出
++attempt;
if (attempt < 3) std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
注意点:
- 把
sleep_for放在重试次数未达上限的条件下,避免最后一次失败后还空等 - 超时判断放在循环条件最前面,确保即使
do_something()执行耗时很长,也能及时跳出 - 不要用
duration_cast把 time_point 转成毫秒数再算差值——既低效又易引入整数截断误差
重试间隔要不要递增?用 std::chrono::milliseconds 还是 std::chrono::duration 模板
固定间隔简单直接;指数退避(如 100ms → 200ms → 400ms)更适合网络请求类场景,能缓解服务端压力。C++11 起,所有时间间隔都应使用具体类型(如 std::chrono::milliseconds),而不是裸 duration 模板。
原因:
- 裸
duration是模板,需显式指定Rep和Period,写起来冗长且易错,例如std::chrono::duration<int, std::milli>不等于std::chrono::milliseconds(后者是 typedef,精度和行为更稳定) - 不同单位混用时(如
seconds+milliseconds),编译器自动推导转换,安全;但若自己用duration_cast强转,可能丢失精度或触发溢出 - 调试时打印日志,
std::chrono::milliseconds可直接用.count()得到整数毫秒值,便于理解
线程休眠的精度陷阱:sleep_for 实际等待时间往往比预期长
操作系统调度不是实时的。std::this_thread::sleep_for(std::chrono::milliseconds(10)) 可能实际挂起 15ms 甚至更久,尤其在负载高、CPU 频率动态调整的机器上。
这意味着:如果你设了 100ms 重试间隔 + 最大 5 次重试,理论最长耗时 500ms,但实际可能接近 700ms。若业务对总耗时敏感(如实时音视频信令),必须预留缓冲。
应对方式:
- 把重试间隔设得略短(如目标 100ms → 实际用 80ms),给调度留余量
- 在循环内用
now() 判断,而非依赖 sleep 精确卡点——哪怕 sleep 多等了,下一轮仍能及时终止 - 避免在单次重试里嵌套多次 sleep(如“每 10ms 查一次状态,最多查 100 次”),这种轮询模式 CPU 开销大且不可靠;优先用事件通知或异步 I/O
真正难处理的是 sleep 时间极短(sleep_for 基本失效,应改用忙等待(std::this_thread::yield())或直接放弃重试,交由上层兜底。


















