如何获取Windows上每个线程的CPU使用率(win32)

时间:2009-09-08 09:40:24

标签: c++ delphi winapi multithreading cpu-usage

寻找Win32 API函数,C ++或Delphi示例代码,它告诉我线程的CPU使用率(百分比和/或总CPU时间)(而不是进程的总和)。我有线程ID。

我知道Sysinternals Process Explorer可以显示这些信息,但我需要在我的程序中提供这些信息。

7 个答案:

答案 0 :(得分:23)

您必须使用这些函数来获取每个线程和进程的cpu使用情况。

GetThreadTimes (检索指定线程的计时信息。)

GetProcessTimes(检索指定进程的计时信息。)

GetSystemTime(检索当前系统日期和时间。系统时间以协调世界时UTC表示)

这是来自Dobb博士Win32 Performance Measurement Options

的优秀文章

再见。

答案 1 :(得分:10)

您使用特定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来查询此信息。

答案 2 :(得分:7)

重要的是要知道在某些情况下,线程的执行时间可能毫无价值。 对于多核系统,每个线程的执行时间通常每15毫秒更新一次,因此如果线程在此时间之前完成其任务,则将重置运行时。 更多细节可以在链接上获得: GetThreadTimes function and I was surprised by the result!
Why GetThreadTimes is wrong

答案 3 :(得分:6)

在上面的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;

答案 4 :(得分:4)

使用“GetThreadTimes”?如果您测量调用“GetThreadTimes”并存储前一个用户和/或内核时间之间的时间,那么您就知道自上次检查以来该线程已经有多长时间了。您还知道平均时间已经过了多少时间,因此您可以计算出使用了多少CPU时间。最好(出于计时器分辨率的原因)每隔一秒左右进行一次检查,并计算出其平均CPU使用率。

答案 5 :(得分:1)

Here 是一个简单的WMI查询包装器。借助它,您可以调用它来获取数据:

getWmiQueryResult(L"SELECT PercentProcessorTime FROM Win32_PerfRawData_PerfProc_Thread WHERE IdProcess=4 and IdThread=8", L"PercentProcessorTime ");

另外,您可能需要查看Win32_PerfRawData_PerfProc_Thread文档,了解您可以获取的其他属性。

答案 6 :(得分:1)

此示例使用GetProcessTimes(),但可以轻松修改为使用GetThreadTimes()

假设将ISO C的clock()除以CLOCKS_PER_SEC,可为您提供经过的CPU秒数。但是,Visual Studio 2019网页显示:

  

时钟功能可显示自从   进程启动期间的CRT初始化。请注意,此功能确实   不严格符合ISO C,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;
}