【问题标题】:How to get the cpu usage per thread on windows (win32)如何在 Windows (win32) 上获取每个线程的 cpu 使用率
【发布时间】:2010-11-26 10:52:18
【问题描述】:

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

我知道 Sysinternals Process Explorer 可以显示此信息,但我的程序中需要此信息。

【问题讨论】:

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


    【解决方案1】:

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

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

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

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

    这里是 Dobb 博士 Win32 Performance Measurement Options 的一篇精彩文章

    再见。

    【讨论】:

    • 另请阅读 lsalamon 的答案,注意 GetThreadTimes 可能“不够准确”
    【解决方案2】:

    您所引用的数据可通过特定的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 来查询这些信息。

    【讨论】:

      【解决方案3】:

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

      【讨论】:

      • 显然在 Vista+ 中,您可以调用 QueryThreadCycleTime 以获得更高的准确性。 stackoverflow.com/questions/5532046/… 我的猜测是,使用 Vista+ GetThreadTimes 也更准确,但待定。
      【解决方案4】:

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

      【讨论】:

      • 但这只是给你线程运行的时间,而不是 CPU 使用率的百分比。您还可以从 WMI 调用中获取总时间以及单个查询中的百分比。
      • 我认为您可以通过每秒轮询来推断 CPU 使用量,如果它的“内核+用户时间”增加 1.0,这意味着它在那一秒内使用了 100% 的一个内核,是吗?
      【解决方案5】:

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

      【讨论】:

        【解决方案6】:

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

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

        您可能还想查看Win32_PerfRawData_PerfProc_Thread 文档以了解您可以获取哪些其他属性。

        【讨论】:

          【解决方案7】:

          此示例使用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;
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-05-07
            • 2016-09-06
            • 1970-01-01
            • 2012-12-25
            • 1970-01-01
            相关资源
            最近更新 更多