有时候需要知道进程各个线程的CPU使用率,以平衡这些线程。
那怎么知道线程的CPU使用率呢?
以下是本人编写的类:
#ifndef _WZ_THREADCONTROL_H
#define _WZ_THREADCONTROL_H
struct ThreadInfo
{
char szThreadName[15];
HANDLE hThread;
int nPercentCPUUsage;
__int64 i64TimeStampLast;
__int64 i64SystemTimeLast;
DWORD dwThreadID;
int nStatus;
};
enum ThreadControlStatus
{
BeginScanThread = 0,
AtScanThread = 1,
EndScanThread = 2,
};
struct IThreadNotify
{
virtual void Notify(int nStatus, ThreadInfo &ti) = 0;
};
class CWzThreadControl
{
public:
static CWzThreadControl *Instant();
static void Release();
void AddThreadNotify(IThreadNotify* pThreadNotify);
void DeleteThreadNotify(IThreadNotify *pThreadNotify);
void Start(HANDLE hModule, DWORD dwModule);
void End();
private:
HWND m_hWnd;
HANDLE m_hModuleCurrent;
DWORD m_dwModuleCurrent;
void* m_mapThreads;
void* m_lstThreadNotify;
CWzThreadControl();
virtual ~CWzThreadControl();
static CWzThreadControl *m_pWzThreadControl;
static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
void OnThreadInfoNotify(ThreadInfo &ti);
void ScanThread();
};
#endif
#include "StdAfx.h"
#include "wzThreadControl.h"
#include <windows.h>
#include <tlhelp32.h>
#include <stdio.h>
#include <map>
#include <list>
#include <algorithm>
using namespace std;
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
CWzThreadControl *CWzThreadControl::m_pWzThreadControl = NULL;
#define DETECTTIMERID WM_USER + 101
CWzThreadControl::CWzThreadControl()
{
m_mapThreads = (void*)new map<DWORD, ThreadInfo>;
m_lstThreadNotify = (void*)new list<IThreadNotify*>;
m_hModuleCurrent = NULL;
m_dwModuleCurrent = -1;
m_hWnd = NULL;
}
CWzThreadControl::~CWzThreadControl()
{
if(m_mapThreads != NULL)
{
delete m_mapThreads;
m_mapThreads = NULL;
}
if(m_lstThreadNotify != NULL)
{
delete m_lstThreadNotify;
m_lstThreadNotify = NULL;
}
}
LRESULT CALLBACK CWzThreadControl::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
if(CWzThreadControl::Instant()->m_hWnd == hwnd && msg == WM_TIMER)
{
CWzThreadControl::Instant()->ScanThread();
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
void CWzThreadControl::ScanThread()
{
if(m_dwModuleCurrent == -1)
{
return;
}
DWORD dwProcessID = m_dwModuleCurrent;
HANDLE hThreadSnap = NULL;
BOOL bRet = FALSE;
THREADENTRY32 te32 = {0};
ThreadInfo ti;
// Take a snapshot of all threads currently in the system.
hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, dwProcessID);
if (hThreadSnap == INVALID_HANDLE_VALUE)
{
return;
}
// Fill in the size of the structure before using it.
te32.dwSize = sizeof(THREADENTRY32);
// Walk the thread snapshot to find all threads of the process.
// If the thread belongs to the process, add its information
// to the display list.
bRet = Thread32First(hThreadSnap, &te32);
HANDLE hThread = NULL;
FILETIME CreationTime;
FILETIME ExitTime;
FILETIME KernelTime;
FILETIME UserTime;
while(bRet)
{
if (te32.th32OwnerProcessID == dwProcessID)
{
// TRACE( "/nTID/t/t%d/n", te32.th32ThreadID);
// TRACE( "Owner PID/t%d/n", te32.th32OwnerProcessID);
// TRACE( "Delta Priority/t%d/n", te32.tpDeltaPri);
// TRACE( "Base Priority/t%d/n", te32.tpBasePri);
GetProcAddress(LoadLibrary("kernel32.dll"), "OpenThread");
__asm
{
push te32.th32ThreadID
push 0
push THREAD_ALL_ACCESS
call eax
mov hThread,eax
}
GetThreadTimes(hThread, &CreationTime, &ExitTime,
&KernelTime, &UserTime);
__int64 i64Time = (KernelTime.dwHighDateTime << 32) + KernelTime.dwLowDateTime +
(UserTime.dwHighDateTime << 32) + UserTime.dwLowDateTime;
map<DWORD, ThreadInfo> *pThread = (map<DWORD, ThreadInfo> *)m_mapThreads;
map<DWORD, ThreadInfo>::iterator itr = pThread->find(te32.th32ThreadID);
if(itr != pThread->end())
{
ThreadInfo ti = itr->second;
__int64 nCurTimestamp = ((__int64)GetTickCount() * 10000);
if(ti.i64SystemTimeLast != 0)
{
// ASSERT(ti.i64TimeStampLast != 0);
ti.nPercentCPUUsage = int(double(i64Time - ti.i64TimeStampLast) * 100.00 /
double(nCurTimestamp - ti.i64SystemTimeLast)) / 2;
// TRACE("nPercentCPUUsage = %d, Name = %s/n",
// ti.nPercentCPUUsage,
// ti.szThreadName);
}
ti.i64SystemTimeLast = nCurTimestamp;
ti.i64TimeStampLast = i64Time;
itr->second = ti;
}
else
{
ThreadInfo ti;
ti.dwThreadID = te32.th32ThreadID;
ti.i64SystemTimeLast = 0;
ti.i64TimeStampLast = 0;
memset(ti.szThreadName, 0, sizeof(ti.szThreadName));
ti.hThread = hThread;
ti.nPercentCPUUsage = 0;
ti.nStatus = -1;
(*pThread)[te32.th32ThreadID] = ti;
}
// for(map<HANDLE, ThreadInfo>::iterator itr = pThread->begin();
// itr != pThread->end(); itr++)
// {
// ThreadInfo ti = itr->second;
// if(ti.dwThreadID == te32.th32ThreadID)
// {
// // SYSTEMTIME syt;
// // GetLocalTime(&syt);
// // CTime tm(syt);
//
// __int64 nCurTimestamp = ((__int64)GetTickCount() * 10000);
//
// if(ti.i64SystemTimeLast != 0)
// {
// ASSERT(ti.i64TimeStampLast != 0);
// ti.nPercentCPUUsage = int(double(i64Time - ti.i64TimeStampLast) * 100.00 /
// double(nCurTimestamp - ti.i64SystemTimeLast)) / 2;
// TRACE("nPercentCPUUsage = %d, Name = %s/n",
// ti.nPercentCPUUsage,
// ti.szThreadName);
// }
//
// ti.i64SystemTimeLast = nCurTimestamp;
// ti.i64TimeStampLast = i64Time;
// itr->second = ti;
// }
// }
// TRACE( "UserTime %d,%d/n", KernelTime.dwHighDateTime, KernelTime.dwLowDateTime);
// TRACE( "UserTime %d%d/n", i64Time);
}
bRet = Thread32Next(hThreadSnap, &te32);
}
// Do not forget to clean up the snapshot object.
CloseHandle (hThreadSnap);
list<IThreadNotify*> *plstThreadNotify = (list<IThreadNotify*>*)m_lstThreadNotify;
for (list<IThreadNotify*>::iterator itrThreadNotify = plstThreadNotify->begin();
itrThreadNotify != plstThreadNotify->end(); itrThreadNotify++)
{
IThreadNotify* pThreadNotify = (*itrThreadNotify);
if (pThreadNotify == NULL)
{
continue;
}
pThreadNotify->Notify(BeginScanThread, ti);
}
map<DWORD, ThreadInfo> *pThread = (map<DWORD, ThreadInfo> *)m_mapThreads;
for(map<DWORD, ThreadInfo>::iterator itr = pThread->begin();
itr != pThread->end(); itr++)
{
ThreadInfo ti = itr->second;
OnThreadInfoNotify(ti);
}
for (itrThreadNotify = plstThreadNotify->begin();
itrThreadNotify != plstThreadNotify->end(); itrThreadNotify++)
{
IThreadNotify* pThreadNotify = (*itrThreadNotify);
if (pThreadNotify == NULL)
{
continue;
}
pThreadNotify->Notify(EndScanThread, ti);
}
return ;
}
void CWzThreadControl::OnThreadInfoNotify(ThreadInfo &ti)
{
list<IThreadNotify*> *plstThreadNotify = (list<IThreadNotify*>*)m_lstThreadNotify;
for (list<IThreadNotify*>::iterator itr = plstThreadNotify->begin();
itr != plstThreadNotify->end(); itr++)
{
IThreadNotify* pThreadNotify = (*itr);
if (pThreadNotify == NULL)
{
continue;
}
pThreadNotify->Notify(AtScanThread, ti);
}
TRACE("ThreadID = %d, PercentCPUUsage = %d/n", ti.dwThreadID, ti.nPercentCPUUsage);
}
void CWzThreadControl::AddThreadNotify(IThreadNotify* pThreadNotify)
{
list<IThreadNotify*> *plstThreadNotify = (list<IThreadNotify*>*)m_lstThreadNotify;
if (pThreadNotify != NULL)
{
plstThreadNotify->push_back(pThreadNotify);
}
}
void CWzThreadControl::DeleteThreadNotify(IThreadNotify *pThreadNotify)
{
list<IThreadNotify*> *plstThreadNotify = (list<IThreadNotify*>*)m_lstThreadNotify;
if(pThreadNotify == NULL)
{
plstThreadNotify->clear();
return;
}
list<IThreadNotify*>::iterator itr = find(plstThreadNotify->begin(), plstThreadNotify->end(), pThreadNotify);
if (itr != plstThreadNotify->end())
{
plstThreadNotify->erase(itr);
}
}
CWzThreadControl *CWzThreadControl::Instant()
{
if(m_pWzThreadControl == NULL)
{
m_pWzThreadControl = new CWzThreadControl;
WNDCLASSEX wc;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = NULL;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = "ThreadControl";
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if(!RegisterClassEx(&wc))
{
return 0;
}
m_pWzThreadControl->m_hWnd = CreateWindowEx(WS_EX_CLIENTEDGE,
"ThreadControl",
"ThreadControl",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT, 0, 0, NULL, NULL, NULL, NULL);
if(m_pWzThreadControl->m_hWnd == NULL)
{
return 0;
}
}
return m_pWzThreadControl;
}
void CWzThreadControl::Release()
{
if(m_pWzThreadControl != NULL)
{
::DestroyWindow(m_pWzThreadControl->m_hWnd);
::SendMessage(m_pWzThreadControl->m_hWnd, WM_CLOSE, 0, 0);
m_pWzThreadControl->m_hWnd = NULL;
delete m_pWzThreadControl;
m_pWzThreadControl = NULL;
}
}
void CWzThreadControl::Start(HANDLE hModule, DWORD dwModule)
{
m_hModuleCurrent = hModule;
m_dwModuleCurrent = dwModule;
SetTimer(m_hWnd, DETECTTIMERID, 500, NULL);
}
void CWzThreadControl::End()
{
KillTimer(m_hWnd, DETECTTIMERID);
map<DWORD, ThreadInfo> *pThread = (map<DWORD, ThreadInfo> *)m_mapThreads;
pThread->clear();
}
1、知道了各个线程的CPU使用率后,可以对各个线程进行平衡,如果发现某个线程使用率太大,可以用SuspendThread暂停线程,以为其他模块交出时间片,用ResumnThread恢复执行。
2、同时可以在此基础上编写检测线程状态的功能,这对于检测死锁问题也有帮助,当发现死锁时马上解锁恢复执行。
3、有时虽然知道的线程ID,但不知道具体是那个线程,这可以在线程创建时,记录该线程和线程ID的对应关系以方便查找。但其实可以在进程中埋下标准接口,而由线程监控模块去获取线程和线程ID的对应关系,这样可以把线程监控模块做成另外一个进程(或者说另外一个工具)。用户使用该工具可以很明显看出那个线程ID对应哪个线程。
有什么问题请加我QQ:363169978(明白),欢迎打扰。