C#系统CPU使用率和与Windows任务管理器同步

C#系统CPU使用率和与Windows任务管理器同步,c#,windows,multithreading,cpu-usage,C#,Windows,Multithreading,Cpu Usage,这是一个由两部分组成的问题,我想在堆栈上发布我的代码,以帮助其他人完成相同的任务 问题1: 我有一个子集的代码,我相信它是按照测量间隔正确地测量CPU使用率的(在系统中,根据检索到的次数,跨尽可能多的内核),我在线程调用中使用1秒 我必须从Web上的很少文章和C++代码中解读这个。我的问题是,对于问题1,我所做的是正确的吗? 有时返回的值是负数,这就是为什么我乘以-1。我再次假设,由于文档很少,这就是我应该做的 我有以下代码: public static class Processor {

这是一个由两部分组成的问题,我想在堆栈上发布我的代码,以帮助其他人完成相同的任务

问题1:

我有一个子集的代码,我相信它是按照测量间隔正确地测量CPU使用率的(在系统中,根据检索到的次数,跨尽可能多的内核),我在线程调用中使用1秒

我必须从Web上的很少文章和C++代码中解读这个。我的问题是,对于问题1,我所做的是正确的吗?

有时返回的值是负数,这就是为什么我乘以-1。我再次假设,由于文档很少,这就是我应该做的

我有以下代码:

public static class Processor
{
    [DllImport("kernel32.dll", SetLastError = true)]
    static extern bool GetSystemTimes(out ComTypes.FILETIME lpIdleTime, out ComTypes.FILETIME lpKernelTime, out ComTypes.FILETIME lpUserTime);

    private static TimeSpan _sysIdleOldTs;
    private static TimeSpan _sysKernelOldTs;
    private static TimeSpan _sysUserOldTs;

    static Processor()
    {
    }

    public static void Test()
    {
        ComTypes.FILETIME sysIdle, sysKernel, sysUser;

        if(GetSystemTimes(out sysIdle, out sysKernel, out sysUser))
        {
            TimeSpan sysIdleTs = GetTimeSpanFromFileTime(sysIdle);
            TimeSpan sysKernelTs = GetTimeSpanFromFileTime(sysKernel);
            TimeSpan sysUserTs = GetTimeSpanFromFileTime(sysUser);

            TimeSpan sysIdleDiffenceTs = sysIdleTs.Subtract(_sysIdleOldTs);
            TimeSpan sysKernelDiffenceTs = sysKernelTs.Subtract(_sysKernelOldTs);
            TimeSpan sysUserDiffenceTs = sysUserTs.Subtract(_sysUserOldTs);

            _sysIdleOldTs = sysIdleTs;
            _sysKernelOldTs = sysKernelTs;
            _sysUserOldTs = sysUserTs;

            TimeSpan system = sysKernelDiffenceTs.Add(sysUserDiffenceTs);

            Double cpuUsage = (((system.Subtract(sysIdleDiffenceTs).TotalMilliseconds) * 100) / system.TotalMilliseconds);

            if (cpuUsage < 0)
            {
                Console.WriteLine("CPU: " + ((int) (cpuUsage)*-1) + "%");
            }
            else
            {
                Console.WriteLine("CPU: " + (int) (cpuUsage) + "%");
            }
            Console.WriteLine("");
        }
        else
        {
            Console.WriteLine("Couldn't get CPU usage!");
            Console.WriteLine("");
        }
    }

    private static TimeSpan GetTimeSpanFromFileTime(ComTypes.FILETIME time)
    {
        return TimeSpan.FromMilliseconds((((ulong)time.dwHighDateTime << 32) + (uint)time.dwLowDateTime) * 0.000001);
    }
}
虽然我仍然不确定这个公式。虽然它看起来非常精确,但它有时会返回一个负数,这就是为什么我将它乘以-1,如果是这样的话。毕竟,没有-2%的CPU使用率这样的事情

更新2

所以我用“System.Diagnostics.PerformanceCounter”做了一个简单的测试。虽然它非常方便,并且完全按照它的意图来做,但它确实会产生开销

以下是我的观察结果:

  • 性能计数器需要更长的时间才能初始化。在我的i7 2.6 Ghz上大约要多3秒钟
  • 性能计数器似乎仅仅通过使用它就增加了大约5MB的RAM使用量。我的意思是:使用上面的代码,我的应用程序的最大内存为7.5MB。使用性能计数器,它在12.5MB时“启动”
  • 在5秒钟的时间里,我的线程运行了5次(每秒一次),我的应用程序的内存增加了1MB,这一增长与时间是一致的,尽管在我的情况下,它确实比开始时高出3-4MB。因此,我的应用程序通常是7.5MB的ram,上面的代码,PC代码稳定在16.5MB的ram,比上面的代码增加了9MB注意:上面的代码不会导致这种增加
因此,如果您的应用程序是以一种资源使用和计时是关键的方式构建的,我建议您不要使用性能计数器,因为这些原因。否则,在没有混乱的情况下继续工作


至于我的应用程序,性能计数器将不利于我的软件的用途。

我认为你的公式中有一个错误。您基本上希望计算CPU使用率,如下所示:

CPU Usage = KernelTimeDiff + UserTimeDiff
            --------------------------------------------
            KernelTimeDiff + UserTimeDiff + IdleTimeDiff
因此,对代码进行如下快速修改:

        //  TimeSpan system = sysKernelDiffenceTs.Add(sysUserDiffenceTs);
        //Double cpuUsage = (((system.Subtract(sysIdleDiffenceTs).TotalMilliseconds) * 100) / system.TotalMilliseconds); 

        TimeSpan totaltime = sysKernelDiffenceTs.Add(sysUserDiffenceTs);
        totaltime = totaltime.Add(sysIdleDifferenceTs);
        int cpuUsage = 100 - (sysIdleDifferenceTs.TotalMilliseconds * 100) / totaltime.TotalMilliseconds;
       Console.WriteLine("CPU: " + cpuUsage + "%");
您最初将cpuUsage声明为“Double”。我不确定您是否需要浮点精度,但在代码中,您肯定只能得到整数精度,因为赋值语句只是进行整数运算。如果您需要更高的计算精度,可以通过混合一些浮点值轻松获得:

Double cpuUsage = 100.0 - (sysIdleDifferenceTs.TotalMilliseconds * 100.0) /totaltime.TotalMilliseconds;

此外,在与任务管理器同步方面。据我所知,任务管理器使用性能计数器。(我怀疑GetSystemTimes在幕后进行性能计数器调用,但可能不是)。我也不知道你为什么不使用性能计数器。“%Process Time”计数器是一个即时采样计数器,它不需要使用以前的结果计算差异。(每个逻辑cpu有一个)。使用PDH助手函数而不是传统的注册表项API来获取它。您可以从非托管C/C++DLL中执行此操作,该DLL将“GetCpuUsage”函数导出回您的C#代码。但我也不知道为什么你不能直接从C#中PInvoke PDH函数。我不知道你说的这个开销。我也不确定我是否理解您提到的“调用下一个结果的延迟”。

您应该一次问一个问题。关于第二个问题,没有同步线程之类的事情。任务管理器可能有一个每秒触发一次的时间。您需要的是一个计时器,它可以在任何时候启动。是的,计时器也可以,以及thread.sleep(1000)或更确切地说thread.sleep(1000-线程处理时间)。但我想你回答了我的第二个问题。如果我无法确定Windows任务管理器何时启动它的进程循环,那么我就不能做我想做的事情。它不是任务关键型的,尽管能够做到这一点会很好。我知道这是一篇老文章,但我需要测量计算机的CPU使用率。我在这篇文章中尝试了这段代码,虽然我的应用程序在60%的时间里准确地报告了正确的使用情况,但在剩下的40%的时间里,它也漏掉了标记。以下是数字:任务管理器读取2,我的应用程序报告从7到9。你有没有遇到过这种不匹配的报告?我会按照你建议的方式来尝试,看看它们是否有什么不同。至于浮点数,不,就像我说的,我还没有整理好。谢谢你的公式修正,我会检查出来,当我到我的机器。也许这就是为什么我有时会得到一个负的结果。我能请你检查一下你的数学吗?我试过你对配方的修改,它总是会带来+50%的标记。根据MSDN,“内核”时间还包括“空闲”时间,这就是为什么从内核时间中减去空闲时间,然后将结果添加到“用户”时间以获得“系统”结果。我修改了“返回CPU使用率”行,并删除了不需要的额外Timespan“System”变量<代码>int cpuUsage=(int)((syskerneldifferences.Add(sysuserdifferences).Subtract(sysidledifferences.totalmillizes)*100.00)/syskerneldifferences.Add(sysuserdifferences.totalmillizes)参见上面我的“更新2”。虽然性能计数器,但在CPU使用情况下调用Next不会花费更多时间(或更长的时间来注意),但是启动它确实需要花费更多的时间,并且GC上的内存分配在每次运行时都会继续增加。如果可能的话,我宁愿不强制GC收集
Double cpuUsage = 100.0 - (sysIdleDifferenceTs.TotalMilliseconds * 100.0) /totaltime.TotalMilliseconds;