【发布时间】:2010-11-26 10:52:18
【问题描述】:
寻找 Win32 API 函数、C++ 或 Delphi 示例代码,告诉我线程的 CPU 使用率(百分比和/或总 CPU 时间)(而不是进程的总数)。我有线程 ID。
我知道 Sysinternals Process Explorer 可以显示此信息,但我的程序中需要此信息。
【问题讨论】:
标签: c++ delphi winapi multithreading cpu-usage
寻找 Win32 API 函数、C++ 或 Delphi 示例代码,告诉我线程的 CPU 使用率(百分比和/或总 CPU 时间)(而不是进程的总数)。我有线程 ID。
我知道 Sysinternals Process Explorer 可以显示此信息,但我的程序中需要此信息。
【问题讨论】:
标签: c++ delphi winapi multithreading cpu-usage
您必须使用这些函数来获取每个线程和进程的 cpu 使用率。
GetThreadTimes (检索指定线程的计时信息。)
GetProcessTimes(检索指定进程的计时信息。)
GetSystemTime(检索当前系统日期和时间。系统时间以协调世界时UTC表示)
这里是 Dobb 博士 Win32 Performance Measurement Options 的一篇精彩文章
再见。
【讨论】:
您所引用的数据可通过特定的WMI 调用获得。您可以查询 Win32_Process 以获取各种进程特定信息,并查询 Win32_PerfFormattedData_PerfProc_Process 以获取线程计数,并给出线程句柄(我相信您正在寻找)您可以查询 Win32_PerfRawData_PerfProc_Thread 以获取处理器时间的百分比用过的。
有一个library available for Delphi 为大多数 WMI 查询提供包装器,但是需要一些实验才能获得您要查找的确切查询。查询语法非常类似于 sql,例如在我的系统上返回 threadid 8 的处理器时间百分比,进程 id 4 是:
SELECT PercentProcessorTime FROM Win32_PerfRawData_PerfProc_Thread
WHERE IdProcess=4 and IdThread=8
大多数提供有关正在运行的进程的统计信息的程序现在都使用 WMI 来查询这些信息。
【讨论】:
重要的是要知道在某些情况下,线程的执行时间可能毫无价值。
对于多核系统,每个线程的执行时间通常每 15 毫秒更新一次,因此如果线程在此时间之前完成其任务,则运行时将被重置。
更多详细信息可以在链接中获得:
GetThreadTimes function and I was surprised by the result!
和
Why GetThreadTimes is wrong
【讨论】:
在上面 RRUZ 的回答的帮助下,我终于想出了这个 Borland Delphi 的代码:
const
THREAD_TERMINATE = $0001;
THREAD_SUSPEND_RESUME = $0002;
THREAD_GET_CONTEXT = $0008;
THREAD_SET_CONTEXT = $0010;
THREAD_SET_INFORMATION = $0020;
THREAD_QUERY_INFORMATION = $0040;
THREAD_SET_THREAD_TOKEN = $0080;
THREAD_IMPERSONATE = $0100;
THREAD_DIRECT_IMPERSONATION = $0200;
THREAD_SET_LIMITED_INFORMATION = $0400;
THREAD_QUERY_LIMITED_INFORMATION = $0800;
THREAD_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED or SYNCHRONIZE or $03FF;
function OpenThread(dwDesiredAccess: DWord;
bInheritHandle: Bool;
dwThreadId: DWord): DWord; stdcall; external 'kernel32.dll';
procedure TForm1.Button1Click(Sender: TObject);
var iii:integer;
handle:thandle;
creationtime,exittime,kerneltime,usertime:filetime;
begin
Handle:=OpenThread(THREAD_SET_INFORMATION or THREAD_QUERY_INFORMATION, False, windows.GetCurrentThreadId);
if handle<>0 then
begin
getthreadtimes(Handle,creationtime,exittime,kerneltime,usertime);
label1.caption:='Total time for Thread #'+inttostr(windows.GetCurrentThreadId)+': '+inttostr( (int64(kerneltime)+int64(usertime)) div 1000 )+' msec';
CloseHandle(Handle);
end;
end;
【讨论】:
使用“GetThreadTimes”?如果您测量对“GetThreadTimes”的调用之间的时间并存储以前的用户和/或内核时间,那么您就会知道自从您上次检查以来线程有多少时间。您还知道平均时间已经过去了多少时间,因此您可以计算出使用了多少 CPU 时间。最好(出于计时器分辨率的原因)每隔一秒左右进行一次检查,并计算出其在那一秒内的平均 CPU 使用率。
【讨论】:
Here 是一个简单的 WMI 查询包装器。借助它,您可以调用它来获取数据:
getWmiQueryResult(L"SELECT PercentProcessorTime FROM Win32_PerfRawData_PerfProc_Thread WHERE IdProcess=4 and IdThread=8", L"PercentProcessorTime ");
您可能还想查看Win32_PerfRawData_PerfProc_Thread 文档以了解您可以获取哪些其他属性。
【讨论】:
此示例使用GetProcessTimes(),但可以轻松修改为使用GetThreadTimes()。
ISO C 的clock() 应该在除以CLOCKS_PER_SEC 时为您提供经过的 CPU 秒数。但是 Visual Studio 2019 网页显示:
时钟功能告诉挂钟时间自 进程启动期间的 CRT 初始化。请注意,此功能确实 不严格符合 ISO C,它指定净 CPU 时间为 返回值。
所以,这是我制作的替身。在 Windows 7/Visual Studio 2017 上,CLOCKS_PER_SEC 为 1000,GetProcessTimes() 仅具有毫秒精度,因此返回 clock_t 而不是 double 不会丢失精度。
clock_t AKClock() {
#ifdef WIN32
FILETIME ftimeCreate, ftimeExit, ftimeKernel, ftimeUser;
SYSTEMTIME stimeKernel, stimeUser;
if ( ! GetProcessTimes( GetCurrentProcess(), &ftimeCreate, &ftimeExit,
&ftimeKernel, &ftimeUser ) ||
! FileTimeToSystemTime( &ftimeKernel, &stimeKernel ) ||
! FileTimeToSystemTime( &ftimeUser, &stimeUser ) ) {
char szError[ 1024 ];
MyLoggingFunction( "AKClock() failed; returning -1: %s",
AKGetLastError( szError, sizeof( szError ) ) );
return (clock_t) -1.0;
}
return (clock_t)
( ( stimeKernel.wHour * 3600.0 +
stimeKernel.wMinute * 60.0 +
stimeKernel.wSecond +
stimeKernel.wMilliseconds / 1000.0 +
stimeUser.wHour * 3600.0 +
stimeUser.wMinute * 60.0 +
stimeUser.wSecond +
stimeUser.wMilliseconds / 1000.0 ) * CLOCKS_PER_SEC );
#else
return clock();
#endif
}
为了完整起见,这里是 AKGetLastError():
void AKstrncpy( char *pszDest, const char *pszSrc, const size_t sDest ) {
strncpy( pszDest, pszSrc, sDest );
pszDest[ sDest - 1 ] = '\0';
}
char* AKGetLastError( char* szBuf, int iBufSize ) {
DWORD errorMessageID = GetLastError();
char* pszError = NULL;
size_t sizeBuf;
if( errorMessageID == 0 ) {
AKstrncpy( szBuf, "(no error)", iBufSize );
return szBuf;
}
sizeBuf = FormatMessageA(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, errorMessageID, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
(LPSTR) &pszError, 0, NULL );
AKstrncpy( szBuf, pszError, iBufSize );
LocalFree( pszError );
int iLen = strlen( szBuf );
if ( strcmp( szBuf + iLen - 2, "\r\n" ) == 0 )
szBuf[ iLen - 2 ] = '\0';
return szBuf;
}
【讨论】: