要求:实现一个计算电脑实时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