• 热门专题

C#透过PerformanceCounter取得特定Process的CPU使用率

作者:Level Up  发布日期:2013-03-18 15:28:48
  • 想要透过PerformanceCounter取得特定Process的CPU使用率,首先我们要理解这部分的数据在PerformanceCounter是怎样分布的。这边我们可以叫出效能监视器后,找到Process分类,可以看到如下画面,所有的Process都有对应的Instance,像是chrome、chrome#1、chrome#11...。

     


     

    所以我们的第一步就是要从Process找到对应的Process Instance Name。但是BCL内建的Process类别中并未有这样的信息,要怎样找到呢?这边可透过另一个名叫ID Process的PerformanceCounter辅助,对照笔者准备的两张图,不难发现该PerformanceCounter的值对应的就是Process的PID。www.it165.net

     


     

     

    这给我们了一个提示,我们可透过这个这个PerformanceCounter反查到Process的Instance Name,像是下面这样:

     

    	private string GetProcessInstanceName(int pid)
    	{
    		var category = new PerformanceCounterCategory("Process");
    
    		var instances = category.GetInstanceNames();
    		foreach (var instance in instances)
    		{
    
    			using (var counter = new PerformanceCounter(category.CategoryName,
    				 "ID Process", instance, true))
    			{
    				int val = (int)counter.RawValue;
    				if (val == pid)
    				{
    					return instance;
    				}
    			}
    		}
    		throw new ArgumentException("Invalid pid!");
    	}

    取得了Process的Instance Name后,CPU的使用率我们就可以透过另一个名为% Processor Time的PerformanceCounter下去取得,像是下面这样:

     

    	private static int GetCpuUsage(int pid)
    	{
    		if (!m_CounterPool.ContainsKey(pid))
    		{
    			m_CounterPool.Add(pid, new PerformanceCounter("Process", "% Processor Time", GetProcessInstanceName(pid)));
    		}
    
    		var lastUpdateTime = default(DateTime);
    
    		m_UpdateTimePool.TryGetValue(pid, out lastUpdateTime);
    
    		var interval = DateTime.Now - lastUpdateTime;
    
    		if (interval.TotalSeconds > 1)
    		{
    			m_CpuUsagePool[pid] = (int)(m_CounterPool[pid].NextValue() / Environment.ProcessorCount);
    		}
    
    		return m_CpuUsagePool[pid];
    	}

    这边要特别注意的是,Query PerformanceCounter的时候,必须要间隔一秒,不然会一直Query到错误的值。

    为了方便重用,依惯例笔者还是稍微整理了一下扩充方法:

     

    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Linq;
    using System.Text;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    
    public static class ProcessExtension
    {
    	#region Private Static Var
    	private static Dictionary<int, PerformanceCounter> _counterPool;
    	private static Dictionary<int, DateTime> _updateTimePool;
    	private static Dictionary<int, int> _cpuUsagePool;
    	#endregion
    
    
    	#region Private Static Property
    	private static Dictionary<int, PerformanceCounter> m_CounterPool
    	{
    		get
    		{
    			return _counterPool ?? (_counterPool = new Dictionary<int, PerformanceCounter>());
    		}
    	}
    
    	private static Dictionary<int, DateTime> m_UpdateTimePool
    	{
    		get
    		{
    			return _updateTimePool ?? (_updateTimePool = new Dictionary<int, DateTime>());
    		}
    	}
    
    	private static Dictionary<int, int> m_CpuUsagePool
    	{
    		get
    		{
    			return _cpuUsagePool ?? (_cpuUsagePool = new Dictionary<int, int>());
    		}
    	}
    	#endregion
    
    
    	#region Private Static Method
    	private static string GetProcessInstanceName(int pid)
    	{
    		var category = new PerformanceCounterCategory("Process");
    
    		var instances = category.GetInstanceNames();
    		foreach (var instance in instances)
    		{
    
    			using (var counter = new PerformanceCounter(category.CategoryName,
    				 "ID Process", instance, true))
    			{
    				int val = (int)counter.RawValue;
    				if (val == pid)
    				{
    					return instance;
    				}
    			}
    		}
    		throw new ArgumentException("Invalid pid!");
    	}
    
    	private static int GetCpuUsage(int pid)
    	{
    		if (!m_CounterPool.ContainsKey(pid))
    		{
    			m_CounterPool.Add(pid, new PerformanceCounter("Process", "% Processor Time", GetProcessInstanceName(pid)));
    		}
    
    		var lastUpdateTime = default(DateTime);
    
    		m_UpdateTimePool.TryGetValue(pid, out lastUpdateTime);
    
    		var interval = DateTime.Now - lastUpdateTime;
    
    		if (interval.TotalSeconds > 1)
    		{
    			m_CpuUsagePool[pid] = (int)(m_CounterPool[pid].NextValue() / Environment.ProcessorCount);
    		}
    
    		return m_CpuUsagePool[pid];
    	}
    	#endregion
    
    
    	#region Public Static Method
    	public static string GetInstanceName(this Process process)
    	{
    		return GetProcessInstanceName(process.Id);
    	}
    
    	public static int GetCpuUsage(this Process process)
    	{
    		return GetCpuUsage(process.Id);
    	}
    	#endregion
    }
    

    已笔者的测试范例来说,撰写起来会像下面这样:

     

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Diagnostics;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    
    namespace WindowsFormsApplication24
    {
    	public partial class Form1 : Form
    	{
    		public Form1()
    		{
    			InitializeComponent();
    		}
    
    		private void Form1_Load(object sender, EventArgs e)
    		{
    			timer1.Interval = 1000;
    			lbxProcess.DisplayMember = "ProcessName";
    			lbxProcess.DataSource = Process.GetProcesses();
    		}
    
    		private void lbxProcess_SelectedIndexChanged(object sender, EventArgs e)
    		{
    			var selectedProcess = lbxProcess.SelectedItem as Process;
    			if (selectedProcess == null)
    				return;
    
    			timer1.Enabled = false;
    			tbxInstanceName.Text = selectedProcess.GetInstanceName();
    			tbxCPU.Text = selectedProcess.GetCpuUsage().ToString();
    			timer1.Enabled = true;
    		}
    
    		private void timer1_Tick(object sender, EventArgs e)
    		{
    			var selectedProcess = lbxProcess.SelectedItem as Process;
    			if (selectedProcess == null)
    				return;
    
    			tbxCPU.Text = selectedProcess.GetCpuUsage().ToString();
    		}
    	}
    }
    

    运行结果:

     


     
     
About IT165 - 广告服务 - 隐私声明 - 版权申明 - 免责条款 - 网站地图 - 网友投稿 - 联系方式
本站内容来自于互联网,仅供用于网络技术学习,学习中请遵循相关法律法规