应使用 PerformanceCounter("Processor", "% Processor Time", "_Total") 获取 CPU 占用率,首次调用 NextValue() 预热后需延迟 ≥1000ms 再取值,返回 0.0–100.0 浮点数;Process.TotalProcessorTime 因无系统时间基准、不支持归一化,无法正确计算百分比。

直接用 PerformanceCounter,别碰 Process.TotalProcessorTime —— 它根本算不出百分比。
为什么 Process.TotalProcessorTime 不能用来算 CPU 占用率
它返回的是 TimeSpan 类型的累计 CPU 时间,不是百分比,也不含系统总时间基准。单独两次相减再除以间隔,结果会严重失真(比如超 100%、负值、跳变剧烈)。这不是精度问题,是数学模型错误。
- 它只反映该进程“自己跑了多久”,不反映“这期间整个系统 CPU 多忙”
- 多核环境下更不可靠:
TotalProcessorTime是所有核心时间之和,但你没法反推系统级归一化分母 - 首次采集无参考点,无法做差分归一化
PerformanceCounter("Processor", "% Processor Time", "_Total") 怎么用才有效
这是 Windows 原生性能计数器,开销低、稳定、无需管理员权限。但必须注意:第一次调用 NextValue() 永远返回 0 或无效值,必须预热 + 延迟后取第二次。
- 构造时指定类别为
"Processor"、计数器名为"% Processor Time"、实例为"_Total" - 创建后立刻调一次
NextValue()(仅预热,丢弃) - 必须等待 ≥500ms(推荐 1000ms),再调第二次
NextValue()才是真实 0–100 的浮点数 - 后续可高频读(如每 500ms),但首次延迟不可省
var cpu = new PerformanceCounter("Processor", "% Processor Time", "_Total");
cpu.NextValue(); // 预热
Thread.Sleep(1000);
float usage = cpu.NextValue(); // 此时才是有效值
监控特定进程 CPU 占用率的关键参数组合
要查某个进程(比如 chrome)的 CPU 占用,不能改用 "Processor" 类别,得切到 "Process" 类别,并严格匹配进程名(区分大小写、带或不带 .exe 后缀需实测)。
- 类别名:
"Process" - 计数器名:
"% Processor Time" - 实例名:
process.ProcessName(例如"chrome",不是"chrome.exe";可用Process.GetProcessesByName("chrome")验证) - 同样需要两次
NextValue()+ 延迟,逻辑和系统级一致 - 注意:如果进程刚启动或已退出,计数器可能抛
InvalidOperationException,需 try/catch
容易被忽略的兼容性与稳定性细节
这套方案只在 Windows 有效(PerformanceCounter 是 Windows API 封装),且依赖系统性能计数器服务正常运行。某些精简版系统或组策略禁用后会失败。
- 首次使用前建议加 fallback:尝试读取失败时,退回到
Environment.ProcessorCount+Stopwatch粗略估算(仅作保底,不推荐用于生产) - 不要为每个采样新建
PerformanceCounter实例——对象创建有开销,应复用 - 如果程序长期运行,建议每小时重新初始化一次计数器,避免底层句柄泄漏累积
-
NextValue()返回值是 float,范围 0.0–100.0,但偶尔会因系统抖动短暂超过 100(比如 100.2),建议截断或过滤











