GetSystemTimes 返回的 idle、kernel、user 时间单位是100纳秒(FILETIME),需用ULARGE_INTEGER转为64位整数计算;CPU使用率=1−(idle差值)/(total差值),total=kernel+user,须两次采样且间隔≥200ms。

GetSystemTimes 返回的 idle、kernel、user 时间到底是什么单位?
它们不是毫秒或秒,而是 100 纳秒为单位的 FILETIME(即 100ns tick),必须先转成 ULARGE_INTEGER 或用 FileTimeToULargeInteger 转成 64 位整数才能做减法。直接强转 DWORD 会因高位截断导致计算结果剧烈跳变甚至负值。
常见错误:把 FILETIME.dwLowDateTime 单独当数值用 —— 这只占低 32 位,Windows 系统运行几小时后就溢出。
正确做法:
ULARGE_INTEGER idle, kernel, user; GetSystemTimes(&idle, &kernel, &user); uint64_t idle_ticks = idle.QuadPart; uint64_t kernel_ticks = kernel.QuadPart; uint64_t user_ticks = user.QuadPart;
CPU 使用率不是 (user + kernel) / total,而是 1 − idle / total
很多人误以为总时间 = kernel_ticks + user_ticks,其实不对。kernel_ticks 包含了 idle 时间(Windows 内核把空闲也记在 kernel 下),所以 kernel_ticks ≥ idle_ticks,且 total_ticks = kernel_ticks + user_ticks 是整个系统自启动以来所有 CPU 时间总和(包括 idle)。
因此真实 CPU 负载率 = 1.0 - (current_idle - last_idle) / (current_total - last_total),其中 total = kernel + user。
关键点:
- 必须做两次采样,间隔至少 200ms(太短会导致除零或精度崩坏)
- 差值必须用 64 位无符号整型,否则减法可能溢出或变负
- 若
current_total == last_total,跳过本次计算(说明没发生调度,或系统太安静)
多核 CPU 下 GetSystemTimes 是否自动聚合?
是的。GetSystemTimes 返回的是全系统所有逻辑处理器的累加值,不需要手动遍历 CPU 核心。它等价于任务管理器“性能”页里显示的“CPU 使用率”总览值(非每个核心单独值)。
但注意:idle 是所有核心空闲时间之和,kernel 和 user 同理。所以公式仍然成立,无需除以核心数。
如果你需要单核负载,得用 QueryProcessCycleTime + GetProcessTimes 配合线程亲和性,或者改用 WMI / ETW,GetSystemTimes 不提供单核粒度。
为什么第一次调用总是返回 0 或极低值?
因为 GetSystemTimes 在某些 Windows 版本(尤其是 Win10 1809+)上首次调用可能未初始化计数器,或内核尚未积累足够调度事件。这不是 bug,是设计行为。
实战建议:
- 首次调用后 sleep 250ms,再取第二组值用于计算
- 忽略前 1–2 次结果(或设默认值 0.0)
- 检查返回值:失败时
GetLastError()通常为ERROR_ACCESS_DENIED(需管理员权限)或ERROR_INVALID_FUNCTION(旧系统不支持,如 XP)
权限问题比想象中常见——即使程序没显式提权,GetSystemTimes 在部分组策略锁定环境下也会静默失败,务必检查返回值。
真正的难点不在公式本身,而在时间戳对齐、类型安全转换、以及 Windows 内核计数器更新节奏的不可控性。别信“一次调用就能出结果”的代码片段。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











