PerformanceCounter 获取 CPU 占用率必须先调用 NextValue() 预热并等待 ≥1 秒后再调用第二次才能获得有效值(0.0–100.0),直接新建即读会返回 0 或错误;需复用实例、正确指定计数器路径,且仅 Windows 支持。
PerformanceCounter 获取 CPU 占用率为什么总是 0 或不准
根本原因是 performancecounter 需要先调用 nextvalue() 两次(间隔至少一秒),第一次是“预热”,第二次才返回有效值;直接新建后立刻读,几乎必然返回 0 或错误值。
- 必须调用两次
NextValue(),且中间要有明显延迟(建议Thread.Sleep(1000)) - 计数器类别名必须是
"Processor",实例名用"_Total"表示整体 CPU,不能写错大小写或漏下划线 - 首次调用前若系统刚启动或计数器未初始化,可能抛
InvalidOperationException,需 try-catch - 在 .NET Core / .NET 5+ 上,
PerformanceCounter在 Linux/macOS 不可用,仅 Windows 支持
C# 中正确的 PerformanceCounter 初始化和读取方式
不要在循环里反复 new PerformanceCounter,它开销大、且可能引发句柄泄漏;应复用实例,并确保及时 Dispose()。
- 推荐封装成单例或静态字段,避免频繁创建销毁
- 构造时指定完整路径:
new PerformanceCounter("Processor", "% Processor Time", "_Total") - 首次
NextValue()后等待 ≥1 秒,再调第二次——这是唯一可靠获取当前值的方法 - 如果需要持续监控,应在定时器回调中执行“等待→读取”逻辑,而非每秒都 new 一个新对象
var cpuCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total");
cpuCounter.NextValue(); // 预热
Thread.Sleep(1000);
float usage = cpuCounter.NextValue(); // 正确值,范围 0.0–100.0
cpuCounter.Dispose();
为什么有时返回负数或超过 100?
这不是 bug,而是计数器采样机制导致的瞬时异常:当系统在两次采样之间发生高频中断、内核时间计算抖动,或存在多核调度偏差时,NextValue() 可能短暂溢出。这不是数据损坏,而是原始性能数据的固有噪声。
- 负值通常出现在系统刚启动、计数器尚未稳定时,跳过前 2–3 次读取可规避
- 超过 100 的值(如 102.3)常见于超线程或频率突变场景,属于合理范围,不应强制截断为 100
- 生产环境建议做简单平滑:取最近 3–5 次值的中位数,比单次读取更稳
- 注意:该计数器反映的是“自上次采样以来”的平均占用,不是严格意义上的“当前毫秒级瞬时值”
.NET 6+ 替代方案:要不要用 System.Diagnostics.Metrics?
不用。目前 System.Diagnostics.Metrics 是为可观测性(OTel)设计的指标导出框架,不提供对 Windows 性能计数器的底层访问能力;它无法替代 PerformanceCounter 获取 CPU 使用率。
- 没有内置 CPU 指标采集器,也没有计划加入
- 第三方库如
Microsoft.Extensions.Diagnostics.HealthChecks也不覆盖此场景 - 若需跨平台,只能退回到
Process.GetCurrentProcess().TotalProcessorTime算进程级占比,但无法获得系统总占用率 - 真正跨平台的方案得靠 P/Invoke 调
GetSystemTimes(Windows)或/proc/stat(Linux),复杂度陡增
实际用的时候,最常被忽略的是“第一次 NextValue 必须丢弃”这个前提——很多人卡在 0 值上调试半天,最后发现只是少等了一秒。别省这一步。










