要求:實現一個計算電腦實時CPU占有率的測試程序,將多種方法獲取結果顯示在對話框上,動態顯示。
實現:
1、新建基於對話框的MFC應用程序,Dialog上添加控件,為控件添加CSting類型變量m_RateResult1、m_RateResult2、m_RateResult3,
2、創建線程類(Thread.h和Thread.cpp),在 ####Dlg.cpp 文件中的初始化函數 OnInitDialog() 中創建計算CPU利用率的線程
// TODO: 在此添加額外的初始化代碼 _beginthread(&CThread::InitCalcuPdh, 0, this); _beginthread(&CThread::InitCalcuGet, 0, this); _beginthread(&CThread::InitCalcuNt, 0, this);
3、線程類中計算CPU利用率
方式一:使用Performance Data Helper(PDH)性能數據助手,獲取CPU利用率
1 void CThread::InitCalcuPdh(void *pDlg) 2 { 3 CCalculateCPURateDlg *pItemDlg = (CCalculateCPURateDlg *)pDlg; 4 if (pItemDlg == NULL) 5 { 6 return; 7 } 8 9 while(1) 10 { 11 //打開查詢(query)句柄 12 HQUERY query; 13 PDH_STATUS status = PdhOpenQuery(NULL, NULL, &query); 14 if (status != ERROR_SUCCESS) 15 { 16 pItemDlg->MessageBox(L"Open Query Error", NULL, MB_OK); 17 continue; 18 } 19 20 //在查詢中加入計數器(counter) 21 HCOUNTER counter; 22 //counter = (HCOUNTER *)GlobalAlloc(GPTR, sizeof(HCOUNTER)); 23 status = PdhAddCounter(query, LPCWSTR(L"\\Processor Information(_Total)\\% Processor Time"), NULL, &counter); 24 if (status != ERROR_SUCCESS) 25 { 26 pItemDlg->MessageBox(L"Add Counter Error", NULL, MB_OK); 27 continue; 28 } 29 30 //收集query的數據,在兩條PdhCollectQueryData()語句之間加入一條Sleep(1000)語句 31 PdhCollectQueryData(query); 32 Sleep(1000); 33 PdhCollectQueryData(query); 34 35 //獲得格式化(可以顯示的)數據 36 PDH_FMT_COUNTERVALUE pdhValue; 37 DWORD dwValue; 38 status = PdhGetFormattedCounterValue(counter, PDH_FMT_DOUBLE, &dwValue, &pdhValue); 39 if (status != ERROR_SUCCESS) 40 { 41 pItemDlg->MessageBox(L"Get Value Error", NULL, MB_OK); 42 continue; 43 } 44 pItemDlg->m_RateResult1.Format(L"%.0lf%%", pdhValue.doubleValue); 45 46 SendMessage(pItemDlg->m_hWnd, WM_UPDATEDATA, FALSE, FALSE); 47 PdhCloseQuery(query); 48 } 49 }
方式二:使用windows 自帶的API(GetSystemTimes)獲取CPU利用率
1 __int64 DiffFileTime(FILETIME time1, FILETIME time2) 2 { 3 __int64 a = time1.dwHighDateTime << 32 | time1.dwLowDateTime; 4 __int64 b = time2.dwHighDateTime << 32 | time2.dwLowDateTime; 5 return (b - a); 6 } 7 void CThread::InitCalcuGet(void *pDlg) 8 { 9 CCalculateCPURateDlg *pItemDlg = (CCalculateCPURateDlg *)pDlg; 10 if (pItemDlg == NULL) 11 { 12 return; 13 } 14 15 while(1) 16 { 17 double cpurate; 18 __int64 idle, kernel, user; 19 FILETIME preidleTime, idleTime; //空閑時間 20 FILETIME prekernelTime, kernelTime; //內核態時間 21 FILETIME preuserTime, userTime; //用戶態時間 22 23 //計算CPU使用率,需要收集兩份樣本,中間用Sleep()函數間隔1s 24 GetSystemTimes(&preidleTime, &prekernelTime, &preuserTime); 25 Sleep(1000); 26 GetSystemTimes(&idleTime, &kernelTime, &userTime); 27 28 idle = DiffFileTime(preidleTime, idleTime); 29 kernel = DiffFileTime(prekernelTime, kernelTime); 30 user = DiffFileTime(preuserTime, userTime); 31 32 if (kernel + user == 0) 33 cpurate = 0.0; 34 else 35 cpurate = abs((kernel + user - idle) * 100 / (kernel + user));//(總的時間-空閑時間)/總的時間=占用cpu的時間就是使用率 36 37 pItemDlg->m_RateResult2.Format(L"%.0lf%%", cpurate); 38 SendMessage(pItemDlg->m_hWnd, WM_UPDATEDATA, FALSE, FALSE); 39 } 40 }
方式三:使用windows API(NtQuerySystemInformation)獲取CPU利用率
1 typedef struct _UINT64_DELTA 2 { 3 ULONG64 Value; 4 ULONG64 Delta; 5 } UINT64_DELTA, *PUINT64_DELTA; 6 7 #define UpdateDelta(DltMgr, NewValue) \ 8 ((DltMgr)->Delta = (NewValue) - (DltMgr)->Value, \ 9 (DltMgr)->Value = (NewValue), (DltMgr)->Delta) 10 11 void CThread::InitCalcuNt(void *pDlg) 12 { 13 CCalculateCPURateDlg *pItemDlg = (CCalculateCPURateDlg *)pDlg; 14 if (pItemDlg == NULL) 15 { 16 return; 17 } 18 19 while(1) 20 { 21 double cpu_rate; 22 ULONG64 total_time = 0; 23 ULONG64 sys_time = 0; 24 static SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION CpuInformation[1024]; 25 static SYSTEM_INFO sys_info; 26 27 static UINT64_DELTA cpu_kernel_delta; 28 static UINT64_DELTA cpu_user_delta; 29 static UINT64_DELTA cpu_idle_delta; 30 static SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION cpu_totals; 31 memset(&cpu_totals, 0, sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)); 32 33 GetSystemInfo(&sys_info); 34 35 NtQuerySystemInformation( 36 SystemProcessorPerformanceInformation, 37 &CpuInformation, 38 sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION) * (ULONG)sys_info.dwNumberOfProcessors, 39 NULL 40 ); 41 42 for (int i = 0; i < (int)sys_info.dwNumberOfProcessors; i++) 43 { 44 SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION& cpu_info = CpuInformation[i]; 45 46 // KernelTime includes idle time. 47 LONGLONG dpc_time = cpu_info.Reserved1[0].QuadPart; 48 LONGLONG interrupt_time = cpu_info.Reserved1[i].QuadPart; 49 cpu_info.KernelTime.QuadPart -= cpu_info.IdleTime.QuadPart; 50 cpu_info.KernelTime.QuadPart += dpc_time + interrupt_time; 51 52 cpu_totals.Reserved1[0].QuadPart += dpc_time; 53 cpu_totals.IdleTime.QuadPart += cpu_info.IdleTime.QuadPart; 54 cpu_totals.Reserved2 += cpu_info.Reserved2; 55 cpu_totals.Reserved1[1].QuadPart += cpu_info.Reserved1[1].QuadPart; 56 cpu_totals.KernelTime.QuadPart += cpu_info.KernelTime.QuadPart; 57 cpu_totals.UserTime.QuadPart += cpu_info.UserTime.QuadPart; 58 } 59 60 UpdateDelta(&cpu_kernel_delta, cpu_totals.KernelTime.QuadPart); 61 UpdateDelta(&cpu_user_delta, cpu_totals.UserTime.QuadPart); 62 UpdateDelta(&cpu_idle_delta, cpu_totals.IdleTime.QuadPart); 63 64 total_time = cpu_kernel_delta.Delta + cpu_user_delta.Delta + cpu_idle_delta.Delta; 65 sys_time = cpu_kernel_delta.Delta + cpu_user_delta.Delta; 66 67 if (total_time) 68 cpu_rate = sys_time * 100.0 / total_time; 69 else 70 cpu_rate = 0.0; 71 pItemDlg->m_RateResult3.Format(L"%.0lf%%", cpu_rate); 72 Sleep(1000); 73 74 SendMessage(pItemDlg->m_hWnd, WM_UPDATEDATA, FALSE, FALSE); 75 } 76 }
4、stdafx.h 中添加
#pragma comment(lib,"pdh.lib") #pragma comment(lib,"ntdll.lib") #include <Pdh.h> #include <PdhMsg.h> #include <Windows.h> #include <Winternl.h>
錯誤:
1、在線程中調用UpdateData函數,在Debug模式下編譯可以通過,但運行時會觸發中斷
原因:MFC對象不支持多線程操作,不能供多個線程進程使用。子線程調用pDlg-> UpdateData(FALSE)時主線程(界面線程)會阻塞,更新必須由它完成,這樣就形成死鎖。更改界面的操作最好用主線程(界面線程),要想在子線程(工作線程)里執行界面的操作,可以通過向主線程發送消息來解決。
解決:
####Dlg.h 中添加
#define WM_UPDATEDATA 10000+1 afx_msg LRESULT OnUpdateData(WPARAM wParam, LPARAM lParam);
####Dlg.cpp 中添加
ON_MESSAGE(WM_UPDATEDATA, OnUpdateData) LRESULT CCalculateCPURateDlg::OnUpdateData(WPARAM wParam, LPARAM lParam) { UpdateData(wParam); return 0; }
Thread.cpp 中添加
SendMessage(pItemDlg->m_hWnd, WM_UPDATEDATA, FALSE, FALSE);
2、在某些環境中exe文件運行失敗
解決:Release使用MT選項編譯,Debug使用MTd選項編譯(同時屬性頁中選擇在靜態庫中使用MFC)
參考博客:
https://www.cnblogs.com/einyboy/archive/2012/06/13/2548243.html
http://www.cnblogs.com/hbccdf/p/get_sys_cpu_usage_and_mem_usage.html