You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

升级至Windows 11后PDH计数器返回错误值的技术求助

Windows 11下PDH计数器CPU使用率异常问题

将系统从Windows 10升级到Windows 11后,原有的C++ CPU监控程序(基于PDH计数器)返回错误值,在三台不同设备上测试结果差异极大:一台返回0%0.2%,一台返回300%400%,还有一台返回7%~15%,无法和Windows 11任务管理器显示的0%~100%利用率匹配。原代码如下:

//Declare variables
double cpuUsage;
PDH_HQUERY cpuQuery;
PDH_HCOUNTER cpuTotal;

//Initialize at the start
PdhOpenQuery(NULL, NULL, &cpuQuery);
PdhAddEnglishCounter(cpuQuery, L"\\Processor(_Total)\\% Processor Time", NULL, &cpuTotal);
PdhCollectQueryData(cpuQuery);

//Code to be called every second
PDH_FMT_COUNTERVALUE counterVal;
PdhCollectQueryData(cpuQuery);
PdhGetFormattedCounterValue(cpuTotal, PDH_FMT_DOUBLE, NULL, &counterVal);
cpuUsage = counterVal.doubleValue;

同时微软官方的PDH示例代码也存在此类不一致问题。

一、Windows 11对PDH计数器的改动

Windows 11调整了\Processor(_Total)\% Processor Time计数器的计算逻辑:该计数器返回的是所有CPU核心的总使用率,而非单核心归一化后的数值。比如4核CPU满负载时,计数器会返回400%,而任务管理器会自动将其除以核心数,显示为100%。另外,原代码本身存在逻辑缺陷(缺少两次采样的间隔等待),在Windows 10中可能因系统调度特性未暴露,升级后被放大。

二、修复现有PDH代码的方法

针对原代码的问题,需要做两处修改:

  1. 第一次采集数据后,必须等待至少1秒再进行第二次采集(PDH计数器依赖两次采样的差值计算使用率)
  2. 将计数器返回值除以CPU核心数,归一化到0%~100%范围

修改后的代码:

// 声明变量
double cpuUsage = 0.0;
PDH_HQUERY cpuQuery;
PDH_HCOUNTER cpuTotal;
SYSTEM_INFO sysInfo;

// 初始化步骤
GetSystemInfo(&sysInfo);
DWORD coreCount = sysInfo.dwNumberOfProcessors;

PDH_STATUS queryStatus = PdhOpenQuery(NULL, NULL, &cpuQuery);
if (queryStatus == ERROR_SUCCESS) {
    queryStatus = PdhAddEnglishCounter(cpuQuery, L"\\Processor(_Total)\\% Processor Time", NULL, &cpuTotal);
    if (queryStatus == ERROR_SUCCESS) {
        // 第一次采集基准数据
        PdhCollectQueryData(cpuQuery);
        // 等待1秒确保有足够的时间差
        Sleep(1000);
    }
}

// 每秒调用的监控代码
PDH_FMT_COUNTERVALUE counterVal;
if (queryStatus == ERROR_SUCCESS) {
    PdhCollectQueryData(cpuQuery);
    PDH_STATUS valueStatus = PdhGetFormattedCounterValue(cpuTotal, PDH_FMT_DOUBLE, NULL, &counterVal);
    if (valueStatus == ERROR_SUCCESS) {
        // 归一化到0-100%
        cpuUsage = counterVal.doubleValue / coreCount;
        // 修正边界值
        cpuUsage = max(0.0, min(100.0, cpuUsage));
    }
}

三、更简便的CPU使用率监控方案

1. 使用GetSystemTimes API(轻量高效)

无需依赖PDH,直接通过系统API获取CPU时间计算使用率,逻辑更简单,结果和任务管理器完全一致:

#include <windows.h>
#include <algorithm> // 用于max/min

double GetCPUUsage() {
    static FILETIME idlePrev, kernelPrev, userPrev;
    FILETIME idleCurr, kernelCurr, userCurr;

    if (!GetSystemTimes(&idleCurr, &kernelCurr, &userCurr)) {
        return 0.0;
    }

    // 将FILETIME转换为ULONGLONG进行差值计算
    auto FileTimeToULL = [](const FILETIME& ft) {
        return ((ULONGLONG)ft.dwHighDateTime << 32) | ft.dwLowDateTime;
    };

    ULONGLONG idleDiff = FileTimeToULL(idleCurr) - FileTimeToULL(idlePrev);
    ULONGLONG totalDiff = (FileTimeToULL(kernelCurr) - FileTimeToULL(kernelPrev)) + 
                          (FileTimeToULL(userCurr) - FileTimeToULL(userPrev));

    double usage = 0.0;
    if (totalDiff > 0) {
        usage = 100.0 * (1.0 - static_cast<double>(idleDiff) / totalDiff);
    }

    // 更新上次采集的时间
    idlePrev = idleCurr;
    kernelPrev = kernelCurr;
    userPrev = userCurr;

    // 确保数值在合理范围
    return std::max(0.0, std::min(100.0, usage));
}

使用说明:第一次调用该函数会返回0.0,之后每隔1秒调用一次,即可得到正确的CPU使用率。

2. WMI性能计数器(适合多指标监控)

如果需要同时监控内存、磁盘等更多系统指标,可以使用WMI的Win32_PerfFormattedData_PerfOS_Processor类获取归一化后的CPU使用率,但实现相对复杂,适合有批量监控需求的场景。


内容的提问来源于stack exchange,提问作者sfaad

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 20:42:20