在.NET下對進程的性能計數可以使用PerformanceCounter,通過該對象可以對進程的CPU,內存等信息進行統計.對於正常使用來說這個對象還是很方便,但對於同一名稱的多個進程進行性能計數那真是可以無比蛋痛...詳細說一下PerformanceCounter對多個同一名稱的進程計數所面對的問題.
應用情況
PerformanceCounter是通過進程實例名來監控計數,但這個實例名是操作系統實時動態分配的,如果同一名稱的進程只有一個那就比較好處理.如果同一名稱的進程有多個那悲劇的事情就來的,隨着同一名稱的進程創建和關閉會影響到其他同一名稱進程的實例名.這樣會導致相應的PerformanceCounter和進程的對應關系完全錯亂...(真是無法理解.NET對這個的實現為什么不用PID)
假設現在有個ams-p.exe應用程序,依次分別打開三次,那對應的進程實例名是:
1)ams-p.exe [pid:7012]
2)ams-p.exe#1 [pid:7013]
3)ams-p.exe#2 [pid:7014]
由於實例名windows系統實時分配的,一旦同一名稱的進程數量發現變化那對應進程的實例名也會變.針對上面情況當2被關閉后那實例對應的關系可以是.
1)ams-p.exe [pid:7012]
3)ams-p.exe#1 [pid:7014]
這樣就會導致3對應的實例名不存在,從而導致PerformanceCounter上的統計錯誤,如果對應同一名稱的進程數量更多,中間其中一個進程關閉那就會有大量的PerformanceCounter統計和進程對應關系就都會存在錯誤的.
解決辦法
雖然有解決方法,但做法都是非常蛋痛的,監控Process的Exite事件然后根據退出情況來調整實例名和PID的對應關系,不過這種方式是存在一些問題,如果其他應用也是這個進程名又是通過其他渠道打開你未及時捕抓綁定Exite事件,那就會導致計數錯誤的悲劇事件發生.而另一種解決方法就比較全面點,在執行所有PerformanceCounter前行把當前系統該名稱的進程信息獲取上載並建立一個實時的實例名和PID對應關系,然后在執行PerformanceCounter后記錄對應實例名的PID計數情況.具體代碼如下:
class CPUCounter:IDisposable { public CPUCounter(string processName) { mProcessName = processName; mTimer = new System.Threading.Timer(GetUsage, null, 1000, 1000); } private System.Threading.Timer mTimer; private string mProcessName; private Dictionary<int, float> mProcessCpuUsage = new Dictionary<int, float>(); private List<CounterItem> mCounters = new List<CounterItem>(); private Dictionary<string, int> mProcessIDs = new Dictionary<string, int>(); public float ProcessUsage(int pid) { float result = 0; mProcessCpuUsage.TryGetValue(pid, out result); return result; } private void OnCreateCounter(string processname) { CounterItem item = mCounters.Find(e => e.ProcessName == processname); if (item == null) { item = new CounterItem(); item.Counter = new PerformanceCounter(); item.Counter.CategoryName = "Processor"; item.Counter.CounterName = "% Processor Time"; item.Counter.InstanceName = processname; } item.Enabled = true; } private void GetUsage(object state) { mProcessIDs.Clear(); Process[] ps = Process.GetProcessesByName(mProcessName); List<CounterItem> disposeditems = new List<CounterItem>(); if (ps.Length == 1) { mProcessIDs.Add(mProcessName, ps[0].Id); OnCreateCounter(mProcessName); } else { for (int i = 1; i < ps.Length; i++) { mProcessIDs.Add(mProcessName + "#" + i, ps[i].Id); OnCreateCounter(mProcessName + "#" + i); } } foreach (CounterItem item in mCounters) { if (item.Enabled) { mProcessCpuUsage[mProcessIDs[item.ProcessName]] = item.Counter.NextValue(); item.Enabled = false; } else { disposeditems.Add(item); } } if(disposeditems.Count>0) foreach (CounterItem item in disposeditems) { mCounters.Remove(item); } } class CounterItem { public string ProcessName { get; set; } public System.Diagnostics.PerformanceCounter Counter { get; set; } public bool Enabled { get; set; } } public void Dispose() { if (mTimer != null) mTimer.Dispose(); } }
如果有更好辦法的同學不防分享一下.