std::this_thread::sleep_for 是首选方案,因它跨平台、毫秒级精度、自动处理中断且无需额外依赖;推荐用 std::chrono::milliseconds 避免浮点陷阱,而 Sleep 和 nanosleep 需手动处理精度与中断问题。

为什么 std::this_thread::sleep_for 是首选方案
标准库的 std::this_thread::sleep_for 在主流平台(Linux/macOS/Windows)上默认提供毫秒级精度,且无需额外依赖。它底层会映射到系统调用(如 nanosleep、SleepEx),现代内核调度器在非极端负载下通常能保证 ±1–15ms 的误差,对绝大多数后台任务、轮询间隔、限频控制已足够。
常见误区是认为它“不够准”就自行封装系统 API —— 实际上手写反而更容易出错,比如忘记处理中断或信号。C++11 起该函数已是跨平台稳定接口,不必绕路。
- 必须包含
<thread>和<chrono> - 时间单位推荐用
std::chrono::milliseconds,避免浮点数隐式转换带来的精度陷阱 - 若传入 0ms,行为是“让出当前时间片”,不等价于忙等待
Windows 下用 Sleep 时的精度陷阱
Win32 的 Sleep 函数名义上支持毫秒,但实际受系统 timer resolution 影响极大。默认分辨率通常是 15.6ms,意味着 Sleep(1) 可能真正挂起 16ms,Sleep(10) 可能变成 16ms 或 31ms —— 这不是 bug,而是 Windows 多媒体计时器未启用时的正常表现。
如果你必须用 Sleep(例如对接旧代码),至少做两件事:
立即学习“C++免费学习笔记(深入)”;
- 调用
timeBeginPeriod(1)提升系统分辨率(需配对timeEndPeriod(1))—— 但这会影响整个进程乃至系统功耗,慎用 - 永远不要假设
Sleep(n)精确等于 n 毫秒;若逻辑依赖严格间隔,改用高精度等待(如WaitForSingleObject+CreateWaitableTimer) -
Sleep(0)不挂起,只触发线程调度,和std::this_thread::yield()行为一致
Linux/macOS 上 nanosleep 的中断处理必须显式重试
POSIX 的 nanosleep 在被信号中断时会提前返回,并通过 rem 参数传出剩余时间。如果忽略这点,一次 Sleep(100) 可能在收到 SIGUSR1 后只休眠了 10ms 就返回,导致逻辑周期错乱。
标准库的 std::this_thread::sleep_for 已内部处理该问题,自动重试直到总时长满足。但若你直接调用 nanosleep,必须手动循环:
struct timespec req = {0, 100'000'000}; // 100ms
struct timespec rem;
while (nanosleep(&req, &rem) == -1 && errno == EINTR) {
req = rem; // 继续睡剩下的
}
注意:clock_nanosleep(支持绝对时间)也有同样问题,不能跳过 EINTR 判断。
跨平台封装时别碰 usleep 和废弃 API
usleep 在 glibc 2.26+ 已标记为 deprecated,且在 macOS 上早已移除;select(nullptr, nullptr, nullptr, nullptr, &tv) 做 sleep 是黑魔法,可读性差、精度低、还可能被信号干扰。这些都不该出现在新代码中。
真正轻量又可靠的跨平台封装只需三行:
#include <thread> #include <chrono> std::this_thread::sleep_for(std::chrono::milliseconds(50));
如果需要更细粒度(如 100 微秒),用 std::chrono::microseconds 即可,底层仍走相同路径,无需条件编译。
真正难处理的是“唤醒时机不可控”的场景:比如想睡 100ms,但期间有数据到达 socket 就立刻退出。这时候得换 poll/epoll/IOCP,而不是纠结 sleep 精度。


















