如何在C#中获取CPU使用情况?

252
我想在C#中获取应用程序的总CPU使用率。我已经找到了许多深入进程属性的方法,但我只想要进程的CPU使用率和像在任务管理器中获得的总CPU使用率。
如何实现这个功能?

2
https://dev59.com/j2445IYBdhLWcg3w1tcS#4680030 - SwDevMan81
60
这怎么会是离题的呢?荒谬。 - Emperor Eto
11个回答

243
你可以使用System.Diagnostics中的PerformanceCounter类。
像这样初始化:
PerformanceCounter cpuCounter;
PerformanceCounter ramCounter;

cpuCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total");
ramCounter = new PerformanceCounter("Memory", "Available MBytes");

像这样消费:

public string getCurrentCpuUsage(){
            return cpuCounter.NextValue()+"%";
}

public string getAvailableRAM(){
            return ramCounter.NextValue()+"MB";
} 

85
好的,但原始来源似乎是这里:http://zamov.online.fr/EXHTML/CSharp/CSharp_927308.html - Matt Refghi
21
根据我的发现,我需要两次使用cpuCounter.NextValue(),并在它们之间使用Sleep(500)。 - Angel.King.47
10
是的,它看起来像是从那个链接复制的,因此提供原始链接作为参考会更好。另一方面,CMS在这里提供答案也很好,因此懒惰的开发人员不必在Google上四处搜索相同的答案。 :o) - BerggreenDK
15
你需要调用.NextValue()两次,并在中间插入System.Threading.Thread.Sleep调用(1000毫秒应该足够)。请参阅 http://blogs.msdn.com/b/bclteam/archive/2006/06/02/618156.aspx 了解更多信息,但高层次的摘要是你需要两个样本才能计算值,而且你需要给操作系统时间来获取这两个样本。 - Cleggy
1
“_Total” 实例不是指特定应用程序的 CPU 使用情况,而是指总 CPU 使用情况。 - Ronak Agrawal
显示剩余7条评论

76

虽然有点超出要求,但我使用额外的计时器代码来跟踪并在CPU使用率持续1分钟或更长时间达到90%或更高时发出警报。

public class Form1
{

    int totalHits = 0;

    public object getCPUCounter()
    {

        PerformanceCounter cpuCounter = new PerformanceCounter();
        cpuCounter.CategoryName = "Processor";
        cpuCounter.CounterName = "% Processor Time";
        cpuCounter.InstanceName = "_Total";

                     // will always start at 0
        dynamic firstValue = cpuCounter.NextValue();
        System.Threading.Thread.Sleep(1000);
                    // now matches task manager reading
        dynamic secondValue = cpuCounter.NextValue();

        return secondValue;

    }


    private void Timer1_Tick(Object sender, EventArgs e)
    {
        int cpuPercent = (int)getCPUCounter();
        if (cpuPercent >= 90)
        {
            totalHits = totalHits + 1;
            if (totalHits == 60)
            {
                Interaction.MsgBox("ALERT 90% usage for 1 minute");
                totalHits = 0;
            }                        
        }
        else
        {
            totalHits = 0;
        }
        Label1.Text = cpuPercent + " % CPU";
        //Label2.Text = getRAMCounter() + " RAM Free";
        Label3.Text = totalHits + " seconds over 20% usage";
    }
}

7
getRAMCounter()在哪里? - Dieter B
10
cpuCounter.NextValue返回一个浮点数。那么为什么要将它赋值给一个dynamic?然后为什么将该dynamic作为object返回?然后为什么尝试在行int cpuPercent = getCPUCounter()中将一个object分配给一个int?(这段代码无法编译。) - Wyck
1
我收到了“找不到PerformanceCounter类型或命名空间名称”的错误提示,而且在System.Threading命名空间中也找不到线程。 - NeoTechni
每个人都保证会得到“找不到类型或命名空间名称PerformanceCounter”的错误。解决方案无效。 - Draif Kroneg

22

在阅读了几个看起来非常复杂的线程后,我想出了这个方法。我需要在一台8核的机器上监控SQL服务器。对于下面的代码,我将"sqlservr"作为appName传入。

private static void RunTest(string appName)
{
    bool done = false;
    PerformanceCounter total_cpu = new PerformanceCounter("Process", "% Processor Time", "_Total");
    PerformanceCounter process_cpu = new PerformanceCounter("Process", "% Processor Time", appName);
    while (!done)
    {
        float t = total_cpu.NextValue();
        float p = process_cpu.NextValue();
        Console.WriteLine(String.Format("_Total = {0}  App = {1} {2}%\n", t, p, p / t * 100));
        System.Threading.Thread.Sleep(1000);
    }
}

看起来它能够正确地测量在我的8核服务器上SQL正在使用的CPU百分比。


1
total_cpu 应该是 PerformanceCounter("Processor"),而不是 PerformanceCounter("Process"),否则你只会得到 100% * 核心数。 - steve cook
3
你在哪里将“done”设置为true?除非我漏看了什么,否则这似乎是一个无限循环:“while(!done){...}” - Manfred
@Manfred 确实是一个无限循环。 - Jenny
1
旧话题,但这只是一条评论。为什么你不使用 while(true)? - 0x0000000000000
我收到了“找不到PerformanceCounter类型或命名空间名称”的错误信息。 - Draif Kroneg

16

没问题,我明白了!谢谢你的帮助!

这是实现它的代码:

private void button1_Click(object sender, EventArgs e)
{
    selectedServer = "JS000943";
    listBox1.Items.Add(GetProcessorIdleTime(selectedServer).ToString());
}

private static int GetProcessorIdleTime(string selectedServer)
{
    try
    {
        var searcher = new
           ManagementObjectSearcher
             (@"\\"+ selectedServer +@"\root\CIMV2",
              "SELECT * FROM Win32_PerfFormattedData_PerfOS_Processor WHERE Name=\"_Total\"");

        ManagementObjectCollection collection = searcher.Get();
        ManagementObject queryObj = collection.Cast<ManagementObject>().First();

        return Convert.ToInt32(queryObj["PercentIdleTime"]);
    }
    catch (ManagementException e)
    {
        MessageBox.Show("An error occurred while querying for WMI data: " + e.Message);
    }
    return -1;
}

改为获取服务器名称而不是选择的变量selectedServer会更好。像这样。string computername = Environment.GetEnvironmentVariable("computername"); - Dave Wang

9

5

CMS做得没错,但是如果您使用Visual Studio中的服务器资源管理器并在性能计数器选项卡中尝试一下,就可以找到很多有用的指标。


3

对于那些仍无法获得与任务管理器相匹配的总CPU使用率数据的人,您应该使用此语句:

new PerformanceCounter("Processor Information", "% Processor Utility", "_Total");

3

这个类会自动每秒钟轮询计数器,并且是线程安全的:

public class ProcessorUsage
{
    const float sampleFrequencyMillis = 1000;

    protected object syncLock = new object();
    protected PerformanceCounter counter;
    protected float lastSample;
    protected DateTime lastSampleTime;

    /// <summary>
    /// 
    /// </summary>
    public ProcessorUsage()
    {
        this.counter = new PerformanceCounter("Processor", "% Processor Time", "_Total", true);
    }

    /// <summary>
    /// 
    /// </summary>
    /// <returns></returns>
    public float GetCurrentValue()
    {
        if ((DateTime.UtcNow - lastSampleTime).TotalMilliseconds > sampleFrequencyMillis)
        {
            lock (syncLock)
            {
                if ((DateTime.UtcNow - lastSampleTime).TotalMilliseconds > sampleFrequencyMillis)
                {
                    lastSample = counter.NextValue();
                    lastSampleTime = DateTime.UtcNow;
                }
            }
        }

        return lastSample;
    }
}

System.DateTime 实际上是一个 8 字节的值类型,这意味着对 DateTime 变量的赋值不是原子性的。在 32 位平台上,此代码不是线程安全的。 - andrewjsaid

2

对我来说,这似乎是有效的。以下是等待处理器达到一定百分比的示例:

var cpuCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total");
int usage = (int) cpuCounter.NextValue();
while (usage == 0 || usage > 80)
{
     Thread.Sleep(250);
     usage = (int)cpuCounter.NextValue();
}

1
当使用量为0时,你为什么在睡觉? - watashiSHUN
我收到了 "找不到类型或命名空间名称 PerformanceCounter" 的错误。 - Draif Kroneg

0

我不喜欢在所有PerformanceCounter解决方案中添加1秒的停顿。相反,我选择使用WMI解决方案。存在1秒的等待/停顿是为了在使用PerformanceCounter时使读取准确。但是,如果您经常调用此方法并刷新此信息,我建议您不要不断遭受延迟...即使考虑使用异步进程来获取它。

我从这里的片段开始Returning CPU usage in WMI using C#,并在我的博客文章中添加了完整的解决方案说明:

Get CPU Usage Across All Cores In C# Using WMI


7
请在此处包含答案,而不是链接到您的博客。 - Herman
@Herman - 我不仅仅是链接到我的博客;我先给出了解释,然后提供了一个链接,以便在此帖子之外提供更深入的答案。 - atconway
6
你博客文章中的解决方案只有12行代码。为什么不在回答中包含它,而是试图让人们访问你的博客? - Herman
@Herman - 点击链接有什么问题吗?它包含了深入的解释,这就是为什么部分需要详细阐述的想法。此外,这已经是7年前的事情了,只是说一下而已。很难记得这篇确切的帖子,当时我还是SO上的新手。 - atconway
4
链接可能会失效,当核心内容内联时,扫描答案要容易得多。抱歉在之前的回复中有些苛刻,我并不是来刁难你的。 :) - Herman
4
我建议在这里复制代码,因为它很短。除此之外,你的页面上有一个bug:Cast<ManagementObject> 中的ManagementObject需要大写,类型是大小写敏感的,而使用Cast<managementobject>的代码无法编译。 - That Marc

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接