如何快速有效地获取各个进程 cpu占用率 的百分比?

吉普赛的歌 2016-08-19 05:35:20
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
Stopwatch sw = new Stopwatch();
sw.Start();

string categoryName = "Process";
string counterName = "% Processor Time";

List<InstanceInfo> list = GetInstanceList(categoryName);
list.RemoveAll(p => p.InstanceName == "_Total" || p.InstanceName == "Idle" );
float total = GetCounterValue(categoryName, counterName, "_Total");
for(int i=0;i<list.Count;i++)
{
InstanceInfo info = list[i];
info.CounterValue = GetCounterValue(categoryName, counterName, info.InstanceName);
info.Percent = Math.Round(info.CounterValue * 100 / total, 2) ;
}

list.Sort();
if (list.Count > 3)
list.RemoveRange(3, list.Count() - 3);

foreach(InstanceInfo info in list)
Console.WriteLine("{0}\r\n-- {1}, {2}%\r\n", info.InstanceName, info.CounterValue, info.Percent);
sw.Stop();
Console.WriteLine("消耗 ms :{0}",sw.ElapsedMilliseconds);
Console.Read();
}

/// <summary>
/// 获取计数器的当前值
/// </summary>
/// <param name="categoryName"></param>
/// <param name="counterName"></param>
/// <param name="instanceName"></param>
/// <returns></returns>
[Microsoft.SqlServer.Server.SqlProcedure]
public static float GetCounterValue(string categoryName, string counterName, string instanceName)
{
PerformanceCounter pc = new PerformanceCounter(categoryName, counterName, instanceName);
pc.ReadOnly = true;
float[] arr = new float[5];

for(int i=0;i<arr.Length;i++)
{
arr[i] = pc.NextValue();
}

return arr.Max();
}


/// <summary>
/// 获取实例列表
/// </summary>
/// <param name="categoryName"></param>
/// <returns></returns>
[Microsoft.SqlServer.Server.SqlProcedure]
public static List<InstanceInfo> GetInstanceList(string categoryName)
{
List<InstanceInfo> resultList = new List<InstanceInfo>();
PerformanceCounterCategory counterCategory = new PerformanceCounterCategory(categoryName);
List<string> strList = new List<string>();
string[] instanceArr = counterCategory.GetInstanceNames();
if (instanceArr == null || instanceArr.Length == 0)
{
return resultList;

}

strList.AddRange(instanceArr);
strList.Sort();
foreach (string str in strList)
{
resultList.Add(new InstanceInfo() { RowNum = resultList.Count + 1, InstanceName = str });
}
return resultList;
}
}

public class InstanceInfo : IComparable
{
public int RowNum { get; set; }
public string InstanceName { get; set; }
public float CounterValue { get; set; }
public double Percent { get; set; }


#region IComparable 成员
public int CompareTo(object obj)
{
if (obj is InstanceInfo)
{
InstanceInfo temp = obj as InstanceInfo;
return temp.CounterValue.CompareTo(this.CounterValue);
}
throw new NotImplementedException("obj is not a ExportParameter!");
}
#endregion
}
}


上面的代码, 能用, 但是本身占用的cpu就比较高, 而且取一次值的话根本就没用, 至少得取 5 次。
在我本机执行完需要 4 秒左右, 消耗大不说, 依次执行, 各个进程的实际占用可能都发生了改变。
如改成并发执行, 则更怕导致程序本身占用 cpu 吓人。

哪位有没有什么好办法?
...全文
614 点赞 收藏 24
写回复
24 条回复
xiaohe96 2018年12月21日
每个程序在不同的时段占用的cup资源是不同的,会随着程序的需求和系统根据任务的优先级进行分配,也就是说程序占用cup资源是动态变化的。如你只是想获取当前时间的利用率,那么只调用一次NextValue就可以了,但是这样没有意义,因为很可能下一秒该程序的占用率就变化很大了,所有要实时监控的话,就要每隔一段时间就调用一次。
回复 点赞
qq_34641006 2018年12月18日
最后结果有问题可能是代码是按照单核处理器计算的,查看自己机器,我的四核,再除以4,挺接近的,不知道是不是这样,只提供一种思路吧
回复 点赞
一介散人 2016年08月26日
每天回帖即可获得10分可用分
回复 点赞
吉普赛的歌 2016年08月23日
引用 18 楼 dongxinxi 的回复:
上面测试顺手写的while(true) + Sleep,你可以用定时器

下面的只是限制了次数, 便于核对, 问题还是很多:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Diagnostics;

namespace ConsoleApplication8
{
class Program
{

static void Main(string[] args)
{
var random = new Random();
var sessionId = Process.GetCurrentProcess().SessionId;
var processes = Process.GetProcesses().Where(p => p.SessionId == sessionId).ToList();
var processInfoList = processes.Select(p => new ProcessInfo(p)).ToList();
int i = 0;
while (true)
{
Thread.Sleep(random.Next(500, 2000));
Console.Clear();
foreach (var item in processInfoList)
{
try
{
if (!item.Process.HasExited)
{
Console.WriteLine("{0}->{1:0.##}%", item.Process.ProcessName, item.CpuPencentage);
}
}
catch (Exception)
{
}
}
i++;
if (i >= 3)
break;
}
Console.Read();
}

class ProcessInfo
{
public ProcessInfo(System.Diagnostics.Process process)
{
this.Process = process;
}

/// <summary>
/// CPU使用数据,用于统计使用率
/// </summary>
[NonSerialized]
protected CpuUsage _cpuUsage;

[NonSerialized]
public readonly Process Process;

public float CpuPencentage
{
get
{
if (this._cpuUsage == null)
{
this._cpuUsage = new CpuUsage(this.Process.TotalProcessorTime.TotalMilliseconds);
return 5f;
}
return (float)this._cpuUsage.GetCpuPencentage(this.Process.TotalProcessorTime.TotalMilliseconds, DateTime.Now);
}
}
}

public class CpuUsage
{
public CpuUsage(double totalCpuTime)
{
this.TotalProcessorTime = totalCpuTime;
this.UpdateTime = DateTime.Now;
}

/// <summary>
/// 上次检测到的CPU占用总时间(毫秒)
/// </summary>
public double TotalProcessorTime;

/// <summary>
/// 上次的检测时间
/// </summary>
public DateTime UpdateTime;

/// <summary>
/// 获取CPU使用率的百分比数
/// </summary>
/// <param name="nextTotalMilliseconds">自上一次采样后的CPU占用毫秒数</param>
/// <param name="nextTime">CPU采样的时间</param>
/// <returns></returns>
public double GetCpuPencentage(double nextTotalMilliseconds, DateTime nextTime)
{
lock (this)
{
var cpuPercentage = (nextTotalMilliseconds - this.TotalProcessorTime)
/ nextTime.Subtract(this.UpdateTime).TotalMilliseconds * 100;
this.TotalProcessorTime = nextTotalMilliseconds;
this.UpdateTime = nextTime;
//Console.WriteLine(cpuPercentage);
return cpuPercentage;
}
}
}

}
}

回复 点赞
IE11下面经常卡到爆 2016年08月23日
兼顾性能和方便,用C++.Net吧,API引个头文件直接就能用,Linq也一样。网上有很多C++开源的任务管理器,可以去看看
回复 点赞
IE11下面经常卡到爆 2016年08月23日
上面测试顺手写的while(true) + Sleep,你可以用定时器
回复 点赞
IE11下面经常卡到爆 2016年08月23日
用法不对,稍改了下

static void Main(string[] args)
        {
            var random = new Random();
            var sessionId = Process.GetCurrentProcess().SessionId;
            var processes = Process.GetProcesses().Where(p => p.SessionId == sessionId).ToList();
            var processInfoList = processes.Select(p => new ProcessInfo(p)).ToList();            
            while (true)
            {
                Thread.Sleep(random.Next(500, 2000));
                Console.Clear();                
                foreach (var item in processInfoList)
                {
                    if (!item.Process.HasExited)
                    {
                        Console.WriteLine("{0}->{1:0.##}%", item.Process.ProcessName, item.CpuPencentage);
                    }                    
                }
            }
            Console.Read();
}

class ProcessInfo
        {
            public ProcessInfo(System.Diagnostics.Process process)
            {
                this.Process = process;                
            }

            /// <summary>
            /// CPU使用数据,用于统计使用率
            /// </summary>
            [NonSerialized]
            protected CpuUsage _cpuUsage;

            [NonSerialized]
            public readonly Process Process;

            public float CpuPencentage
            {
                get
                {
                    if (this._cpuUsage == null)
                    {
                        this._cpuUsage = new CpuUsage(this.Process.TotalProcessorTime.TotalMilliseconds);
                        return 5f;
                    }
                    return (float)this._cpuUsage.GetCpuPencentage(this.Process.TotalProcessorTime.TotalMilliseconds, DateTime.Now);
                }
            }
        }
没有考虑新开启的进程。你可以用轮询,然后将采样到的Processes跟processInfoList对比查找 所以之前的代码中没有Process Process属性,只需要记个int PID作查找用 对于新开启的进程,可以用系统钩子监测主窗口开启的通知(不需要轮询了) 但对于没有界面的进程会比较复杂,控制台程序是当前用户的,对于system服务Process.HasExited会报错 主要是OpenProcess没权限取句柄,而且对于 360这样的程序可以在这里挂线程钩子或者驱动层拦截(从任务管理器你干不掉它) 因为我之前写的是window服务,基本没那些问题。刚一到控制台里跑就发现有些报错 所以为测试,我上面只取当前会话的进程 Process.GetProcesses().Where(p => p.SessionId == sessionId) 你可以用(p => p.Id > 4)来取所有的(PID=4的System进程特殊一点,通常可不考虑)
回复 点赞
bigbaldy 2016年08月23日
第一次肯定慢,同样代码第二次执行提速4倍,还有中间很多PerformanceCounter都能缓存,实例列表也不需要每次都调用,反正优化的点非常多,而且我测试了,就算不优化,第二次执行我的机器耗时300毫秒,而且writeline本身也是耗时的,任务管理器一秒刷新一次,而且只是部分刷新,所以如果你真写一个任务管理器,就按现在的方法,根本不会慢,也不会占CPU
回复 点赞
IE11下面经常卡到爆 2016年08月23日
理论上公式没有什么问题 可以这么理解,1秒钟等效于10000个CPU周期(当然,实际远不止这个数量级) 你的进程总共占用了其中的100个CPU周期,使用率就是 100/10000 = 1% 这个好理解吧 问题主要是因为现在的CPU都是多核多线程的,所以只能算出每核占用的平均值 var cpuPercentage = (nextTotalMilliseconds - this.TotalProcessorTime) / nextTime.Subtract(this.UpdateTime).TotalMilliseconds * 100 / Environment.ProcessorCount 我没有去看Process.TotalProcessorTime的代码,内部应该也是通过API去查询到每个核心占用的时间总和
回复 点赞
Poopaye 2016年08月22日
引用 2 楼 yenange 的回复:
大哥, 你这种做法, 就算能用, 快得起来吗?
你是不是没见过任务管理器啊
回复 点赞
吉普赛的歌 2016年08月22日
引用 5 楼 Libby1984 的回复:
[quote=引用 4 楼 yenange 的回复:] [quote=引用 3 楼 Libby1984 的回复:] [quote=引用 2 楼 yenange 的回复:] [quote=引用 1 楼 shingoscar 的回复:]
for(int i=0;i<arr.Length;i++) 
{
    arr[i] = pc.NextValue();
}
首先像这种写法上就错误了,要统计cpu,流程应该是: 1、为每个instance创建performancecounter 2、等待一段时间 3、调用nextvalue并取值 4、重复第2步
大哥, 你这种做法, 就算能用, 快得起来吗?[/quote] 每个程序在不同的时段占用的cup资源是不同的,会随着程序的需求和系统根据任务的优先级进行分配,也就是说程序占用cup资源是动态变化的。如你只是想获取当前时间的利用率,那么只调用一次NextValue就可以了,但是这样没有意义,因为很可能下一秒该程序的占用率就变化很大了,所有要实时监控的话,就要每隔一段时间就调用一次。[/quote] 那任务管理器的列出所有进程的 cpu 占用百分比 的列表是如何实现的?[/quote] 就是按照2楼的方法实现的,开一个线程,每隔一段时间获取cup利用率显示。你没发现任务管理器里面显示的cpu利用率是不断变化的吗。而且在顶部“查看”菜单里面有一个更新速度的设置项。[/quote] 亲爱的, 你自己测试一下就知道这个做法根本不靠谱了
回复 点赞
Milo米啊米啊米 2016年08月22日
引用 4 楼 yenange 的回复:
[quote=引用 3 楼 Libby1984 的回复:] [quote=引用 2 楼 yenange 的回复:] [quote=引用 1 楼 shingoscar 的回复:]
for(int i=0;i<arr.Length;i++) 
{
    arr[i] = pc.NextValue();
}
首先像这种写法上就错误了,要统计cpu,流程应该是: 1、为每个instance创建performancecounter 2、等待一段时间 3、调用nextvalue并取值 4、重复第2步
大哥, 你这种做法, 就算能用, 快得起来吗?[/quote] 每个程序在不同的时段占用的cup资源是不同的,会随着程序的需求和系统根据任务的优先级进行分配,也就是说程序占用cup资源是动态变化的。如你只是想获取当前时间的利用率,那么只调用一次NextValue就可以了,但是这样没有意义,因为很可能下一秒该程序的占用率就变化很大了,所有要实时监控的话,就要每隔一段时间就调用一次。[/quote] 那任务管理器的列出所有进程的 cpu 占用百分比 的列表是如何实现的?[/quote] 就是按照2楼的方法实现的,开一个线程,每隔一段时间获取cup利用率显示。你没发现任务管理器里面显示的cpu利用率是不断变化的吗。而且在顶部“查看”菜单里面有一个更新速度的设置项。
回复 点赞
吉普赛的歌 2016年08月22日
引用 3 楼 Libby1984 的回复:
[quote=引用 2 楼 yenange 的回复:] [quote=引用 1 楼 shingoscar 的回复:]
for(int i=0;i<arr.Length;i++) 
{
    arr[i] = pc.NextValue();
}
首先像这种写法上就错误了,要统计cpu,流程应该是: 1、为每个instance创建performancecounter 2、等待一段时间 3、调用nextvalue并取值 4、重复第2步
大哥, 你这种做法, 就算能用, 快得起来吗?[/quote] 每个程序在不同的时段占用的cup资源是不同的,会随着程序的需求和系统根据任务的优先级进行分配,也就是说程序占用cup资源是动态变化的。如你只是想获取当前时间的利用率,那么只调用一次NextValue就可以了,但是这样没有意义,因为很可能下一秒该程序的占用率就变化很大了,所有要实时监控的话,就要每隔一段时间就调用一次。[/quote] 那任务管理器的列出所有进程的 cpu 占用百分比 的列表是如何实现的?
回复 点赞
Milo米啊米啊米 2016年08月22日
引用 2 楼 yenange 的回复:
[quote=引用 1 楼 shingoscar 的回复:]
for(int i=0;i<arr.Length;i++) 
{
    arr[i] = pc.NextValue();
}
首先像这种写法上就错误了,要统计cpu,流程应该是: 1、为每个instance创建performancecounter 2、等待一段时间 3、调用nextvalue并取值 4、重复第2步
大哥, 你这种做法, 就算能用, 快得起来吗?[/quote] 每个程序在不同的时段占用的cup资源是不同的,会随着程序的需求和系统根据任务的优先级进行分配,也就是说程序占用cup资源是动态变化的。如你只是想获取当前时间的利用率,那么只调用一次NextValue就可以了,但是这样没有意义,因为很可能下一秒该程序的占用率就变化很大了,所有要实时监控的话,就要每隔一段时间就调用一次。
回复 点赞
吉普赛的歌 2016年08月22日
引用 1 楼 shingoscar 的回复:
for(int i=0;i<arr.Length;i++) 
{
    arr[i] = pc.NextValue();
}
首先像这种写法上就错误了,要统计cpu,流程应该是: 1、为每个instance创建performancecounter 2、等待一段时间 3、调用nextvalue并取值 4、重复第2步
大哥, 你这种做法, 就算能用, 快得起来吗?
回复 点赞
吉普赛的歌 2016年08月22日
引用 14 楼 dongxinxi 的回复:
CPU总占用时间2 -CPU总占用时间1
采样时间2 - 采样时间1

转成百分比,乘上100d就可以了


下面是按你的弄的, 感觉还是有问题:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace ConsoleApplication6
{
class Program
{
static void Main(string[] args)
{
ProcessInfo info = new ProcessInfo();
info.GetResult();

Console.ReadLine();
}


class ProcessInfo
{
public void GetResult()
{
foreach (Process item in Process.GetProcesses())
{
try
{
GetCpuPencentage(item);
}
catch (Exception ex)
{
continue;
}
Console.WriteLine("{0}->{1}", item.ProcessName, GetCpuPencentage(item));
//Console.WriteLine();
}
}

/// <summary>
/// CPU使用数据,用于统计使用率
/// </summary>
[NonSerialized]
protected CpuUsage _cpuUsage;

public float GetCpuPencentage(System.Diagnostics.Process process)
{
if(this._cpuUsage == null)
{
this._cpuUsage = new CpuUsage(process.TotalProcessorTime.TotalMilliseconds);
return 5f; //首次检测的值可忽略,这里随便给个值
}
return (float)this._cpuUsage.GetCpuPencentage(process.TotalProcessorTime.TotalMilliseconds, DateTime.Now);
}
}

public class CpuUsage
{
public CpuUsage(double totalCpuTime)
{
this.TotalProcessorTime = totalCpuTime;
this.UpdateTime = DateTime.Now;
}

/// <summary>
/// 上次检测到的CPU占用总时间(毫秒)
/// </summary>
public double TotalProcessorTime;

/// <summary>
/// 上次的检测时间
/// </summary>
public DateTime UpdateTime;

/// <summary>
/// 获取CPU使用率的百分比数
/// </summary>
/// <param name="nextTotalMilliseconds">自上一次采样后的CPU占用毫秒数</param>
/// <param name="nextTime">CPU采样的时间</param>
/// <returns></returns>
public double GetCpuPencentage(double nextTotalMilliseconds, DateTime nextTime)
{
lock (this)
{
var cpuPercentage = (nextTotalMilliseconds - this.TotalProcessorTime)
/ nextTime.Subtract(this.UpdateTime).TotalMilliseconds * 100;
this.TotalProcessorTime = nextTotalMilliseconds;
this.UpdateTime = nextTime;
//Console.WriteLine(cpuPercentage);
return cpuPercentage;
}
}
}
}
}


回复 点赞
IE11下面经常卡到爆 2016年08月22日
CPU总占用时间2 -CPU总占用时间1 采样时间2 - 采样时间1 转成百分比,乘上100d就可以了
回复 点赞
IE11下面经常卡到爆 2016年08月22日
public float GetCpuPencentage(System.Diagnostics.Process process) { if(this.this._cpuUsage == null) { this._cpuUsage = new CpuUsage(process.TotalProcessorTime.TotalMilliseconds); return 5f; //首次检测的值可忽略,这里随便给个值 } return (float)this._cpuUsage.GetCpuPencentage(process.TotalProcessorTime.TotalMilliseconds, DateTime.Now); } 不过,一般应该在构造函数中初始化this._cpuUsage = new CpuUsage(process.TotalProcessorTime.TotalMilliseconds);
回复 点赞
吉普赛的歌 2016年08月22日
引用 11 楼 dongxinxi 的回复:
因为懒,没去写博文,印象中这已经不是第一次回这个问题
谢谢, 我先看下
回复 点赞
IE11下面经常卡到爆 2016年08月22日
因为懒,没去写博文,印象中这已经不是第一次回这个问题
回复 点赞
发动态
发帖子
C#
创建于2007-09-28

8.5w+

社区成员

64.0w+

社区内容

.NET技术 C#
社区公告
暂无公告