这是一个两部分问题,我想在堆栈上发布我的代码,以帮助其他人完成同样的任务。
问题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:
在我的程序中,为了将测量数据(如CPU使用率与上述代码)匹配,我是否仍然可以将我的程序中的线程同步?
我的意思是,如果你打开Windows任务管理器,你会发现它每秒轮询一次 - 实际上它不需要小于那个。我想要做的是将时间与我的线程相匹配。
因此,当Windows任务管理器轮询时,我的线程会轮询。
有些说明:
我不想使用性能计数器或.NET内置方法。事实上,我相信 - 从我所读到的,.NET没有计算机器上CPU使用率的方法,否则就需要性能计数器。
性能计数器有开销,另外使GC增长,更不用说调用下一个结果的延迟。虽然我的软件不需要是实时性能,但我确实需要它作为响应并尽可能少地使用CPU时间。上述代码可以在不到一毫秒的时间内调用和返回。实际上在我的开发机器上,时间跨度差异显示为0ms。 我不相信性能计数器具有响应性。
如果您感到好奇,我的软件会收集许多项目,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”做了一个简单的测试。虽然非常方便,并且完全符合预期目的,但确实会产生开销。
以下是我的观察:
因此,如果您的应用程序是以资源使用和时间关键的方式构建的,那么我建议不要使用性能计数器,因为这些原因。否则继续前进,因为它没有任何混乱。
至于我的应用程序,性能计数器将不利于我的软件目的。
答案 0 :(得分:1)
我认为你的公式中有一个错误。您希望基本上计算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声明为&#34; Double&#34;。我不确定你是否想要浮点精度,但是在你的代码中,你肯定没有得到除整数精度之外的任何东西,因为赋值语句只是在进行整数运算。如果你需要更高的计算精度,你可以通过混合一些浮点来轻松获得它:
Double cpuUsage = 100.0 - (sysIdleDifferenceTs.TotalMilliseconds * 100.0) /totaltime.TotalMilliseconds;
此外,关于与任务管理器同步。据我所知,任务管理器使用perf计数器。 (而且我怀疑GetSystemTimes正在进行全面反击调用,但可能没有)。而且我不确定为什么你也不会使用perf计数器。 &#34;%处理时间&#34; counter是一个即时样本计数器,不需要计算具有先前结果的差异。 (每个逻辑cpu有一个)。使用PDH帮助程序函数而不是旧的注册表项apis来获取它。您可以从导出&#34; GetCpuUsage&#34;的非托管C / C ++ DLL中执行此操作。功能回到你的C#代码。但我不知道你为什么不能从C#中启动PDH功能。我不知道你说的这个开销。我不确定我是否理解你对&#34;呼叫下一个结果的延迟&#34;任