CPU占用率=(进程时间差/系统工作时间差)×100/逻辑处理器数,需用GetSystemTimes与GetProcessTimes两次采样(间隔250ms),FILETIME须用ULARGE_INTEGER转毫秒,避免DWORD截断导致跳变。
用
和
算 CPU 占用率,不是读注册表或 WMI
Windows 下没有现成的“当前进程 CPU 百分比” API,必须靠时间差推算。核心是:CPU 利用率 = 进程总用户态+内核态时间 / 系统总非空闲时间 × 100%。关键在于两次采样间隔要够短(通常 200–500ms),且必须用
获取系统级时间片,不能只看进程自己的
—— 否则会漏掉系统空闲时间基准,结果严重偏高。
常见错误是直接拿两次
的差值除以采样间隔,这算出来的是“进程运行时间占比”,不是“CPU 占用率”。它把进程休眠、阻塞、等待 I/O 的时间也当成了空闲 CPU,导致数值虚低甚至为 0,尤其在轻负载时完全失真。
返回三个
:空闲时间、内核时间(含空闲)、用户时间;真正的系统总工作时间 = 内核时间 + 用户时间 − 空闲时间
返回的内核+用户时间之和,就是该进程实际占用 CPU 的 tick 总和
两次采样间隔建议设为
ms,太短(1s)无法反映瞬时波动
FILETIME 转毫秒差值必须用
,别用
截断
是 64 位结构体,低 32 位 + 高 32 位。直接 cast 成
或
会丢高位,导致时间差突变为负数或极小值,CPU 率跳变到 0% 或 100%。正确做法是用
union 解包:
注意:所有时间差计算(进程时间差、系统工作时间差)都必须用
,否则在长时间运行的进程上,累计的
值很快溢出
范围。
立即学习
“
C++免费学习笔记(深入)
”;
单核 vs 多核 CPU 要除以逻辑处理器数量
算出来的百分比默认是“占单个核心的百分比”。比如四核机器上一个线程满载,
差值 / 系统工作时间差值 ≈ 100%,但这其实是“占用了 1 个核的 100%”,对应整机 CPU 利用率为 25%。所以最终公式是:
C知道
CSDN推出的一款AI技术问答工具
下载
CPU%
= (进程时间差 / 系统工作时间差) × 100 /
不除这个数,多核机器上报的数值永远偏高。别信“WMI 返回的就是整机百分比”——WMI 底层也是这么算的,只是封装好了。自己实现时漏掉这步,监控图表就会持续显示 80%+,哪怕机器明显空闲。
调用
一次即可,结果缓存,无需每次重查
虚拟机或容器环境下,
可能小于物理核数,以该值为准
如果目标是“该进程占用了多少个核”,就不要除;如果是“占整机 CPU 的百分比”,必须除
第一次采样后必须 sleep,否则两帧时间差接近 0 导致除零或 INF
初始化时调一次
和
,然后必须
再采第二次。没 sleep 就直接算,差值基本为 0,分母趋近于 0,结果不是
就是极大噪声值。更隐蔽的问题是:如果进程刚启动,前几次采样可能因内核调度延迟导致
返回的时间差异常小(比如 1ms),此时强行计算会得出 300% 这类非法值。
实操建议:
连续 3 次采样结果若 >100%,先丢弃,等系统稳定再计入
用
替代
,避免阻塞整个线程池(如用在服务进程中)
不要在 GUI 线程里做这个循环,会卡界面;开独立 worker 线程,用
或线程安全队列传结果
真正难的不是公式,而是时间采样节奏和数值稳定性控制。系统时间精度、进程调度抖动、多核计时器偏移都会引入噪声,简单平均或硬限幅反而掩盖问题——得留出调试输出打点,比如把原始
差值全打出来,才能分清是算法错还是数据毛刺。
GetSystemTimesGetProcessTimesGetSystemTimesGetProcessTimesGetProcessTimesGetSystemTimesFILETIMEGetProcessTimes250ULARGE_INTEGERDWORDFILETIMEDWORDunsigned longULARGE_INTEGERULARGE_INTEGER ft;
ft.LowPart = filetime.dwLowDateTime;
ft.HighPart = filetime.dwHighDateTime;
LONGLONG ms = ft.QuadPart / 10000LL; // FILETIME 单位是 100ns
LONGLONGFILETIMEDWORDGetProcessTimesGetSystemInfo().dwNumberOfProcessorsGetSystemInfodwNumberOfProcessorsGetProcessTimesGetSystemTimesSleep(250)INFGetSystemTimesstd::this_thread::sleep_for(std::chrono::milliseconds(250))SleepPostMessageFILETIME