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

本文关键字:同步 Windows 任务管理器 系统 CPU | 更新日期: 2023-09-27 18:06:54

这是一个由两部分组成的问题,我想把我的代码放在这里的堆栈中,以帮助其他人完成同样的任务。

问题1:

我有一个代码子集,我相信它可以根据测量间隔正确地测量CPU使用情况(系统中有多少个内核,每次检索次数)-我在线程调用中使用1秒。

我不得不从网上很少的文章和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);
    }
}

问题2:

无论如何我同步一个线程,在我的程序,与Windows任务管理器,以匹配的测量数字,例如CPU使用上述代码的目的?

我的意思是,如果你打开Windows任务管理器,你会注意到它每秒钟轮询一次——实际上它不需要比这少。我想做的是将时间与我的线程匹配。

所以当Windows任务管理器轮询时,我的线程轮询。


一些笔记:

我不想使用性能计数器或。net内置方法。事实上,我相信——从我所读到的,. net没有计算机器上CPU使用情况的方法,否则就需要性能计数器。

性能计数器有开销,并且使GC增长,更不用说调用下一个结果的延迟。虽然我的软件不需要实时性能,但我确实需要它具有响应性,并使用尽可能少的CPU时间。上述代码可以在不到一毫秒的时间内调用和返回。实际上,在我的开发机器上,时间跨度差显示为0毫秒。

如果你很好奇,我的软件正在收集一些项目,CPU,内存,事件日志项目等,这些都需要被收集和存储,在SQL CE,在下一次投票之前,1秒的距离。然而,每个任务项都有自己的线程来实现这一点。

同样,上面的代码无论如何都没有优化,你会注意到我还没有注释它。原因是我想在优化之前确保它是正确的。

更新1

根据我的评论,我删除了额外的"系统"时间跨度,因为它不是必需的,并修改了检索"CPU使用率"的行,并适当地转换它。

int cpuUsage = (int)(((sysKernelDifferenceTs.Add(sysUserDifferenceTs).Subtract(sysIdleDifferenceTs).TotalMilliseconds) * 100.00) / sysKernelDifferenceTs.Add(sysUserDifferenceTs).TotalMilliseconds);

虽然我仍然不确定公式。虽然它似乎是高度准确的,它偶尔会返回一个负号,这就是为什么我乘以-1,如果是这种情况。毕竟,不存在-2%的CPU使用率等情况。

更新2

所以我用"System.Diagnostics.PerformanceCounter"做了一个简单的测试。虽然它非常方便,并且完全符合预期,但它确实会产生开销。

以下是我的观察:

  • 性能计数器的初始化时间太长了。在我的i7 2.6 Ghz上大约多三秒。
  • 性能计数器似乎也增加了另外大约5MB的RAM使用,仅仅通过使用它。我的意思是:使用上面的代码,我的应用程序的最大内存为7.5MB。对于性能计数器,它"开始"在12.5MB。
  • 在5秒的时间里,我的线程每秒运行5次,我的应用程序的内存增长了1 MB,这种增长与时间一致,尽管它确实稳定,在我的情况下,比开始时高3-4MB。所以我的应用通常是7.5MB ram,而PC代码则是16.5 MB ram——比上面的代码增加了9MB。注意:上面的代码引起这个增加。

因此,如果您的应用程序是以资源使用和时间为关键的方式构建的,由于这些原因,我建议不要使用性能计数器。否则,请继续使用,因为它可以正常工作,而不会产生任何混乱。

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

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

我想你的公式有问题。你想要这样计算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在底层进行了性能计数器调用,但也许不是)。我也不知道你为什么不用性能计数器。"%进程时间"计数器是一个即时样本计数器,不需要计算与先前结果的差异。(每个逻辑cpu有一个)。使用PDH helper函数而不是遗留的注册表键api来实现它。你可以从一个非托管的C/c++ DLL中导出一个"GetCpuUsage"函数返回到你的c#代码中。但是我不知道为什么你不能从c#中调用PDH函数。我不知道你说的开销。我不确定我是否理解你所说的"延迟调用下一个结果"。