web-dev-qa-db-ja.com

WindowsでスレッドごとのCPU使用率を取得する方法(win32)

Win32 API関数、スレッドのCPU使用率(パーセントおよび/または合計CPU時間)(プロセスの合計ではない)を教えてくれるC++またはDelphiのサンプルコードを探しています。スレッドIDを持っています。

Sysinternals Process Explorerがこの情報を表示できることは知っていますが、プログラム内でこの情報が必要です。

19
Dirk Paessler

上記のRRUZの答えの助けを借りて、私はついにBorlandDelphiの次のコードを思いつきました。

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;
6
Dirk Paessler

スレッドおよびプロセスごとのCPU使用率を取得するには、これらの関数を使用する必要があります。

GetThreadTimes (指定されたスレッドのタイミング情報を取得します。)

GetProcessTimes (指定されたプロセスのタイミング情報を取得します。)

GetSystemTime (現在のシステム日付と時刻を取得します。システム時刻は協定世界時UTCで表されます)

Dobb博士の優れた記事 Win32パフォーマンス測定オプション

さようなら。

23
RRUZ

参照しているデータは、特定の [〜#〜] wmi [〜#〜] 呼び出しを使用して利用できます。 Win32_Processをクエリしてあらゆる種類のプロセス固有の情報を取得し、Win32_PerfFormattedData_PerfProc_Processをクエリしてスレッド数を取得し、スレッドへのハンドル(私が探していると思うもの)を指定すると、クエリを実行できます Win32_PerfRawData_PerfProc_Thread 使用されたプロセッサ時間の割合。

Delphiで利用可能なライブラリ があり、ほとんどのWMIクエリのラッパーを提供しますが、探している正確なクエリを取得するには、ある程度の実験が必要です。クエリ構文は非常にSQLに似ています。たとえば、私のシステムでは、プロセスID4のスレッドID8のプロセッサ時間の割合を返します。

SELECT PercentProcessorTime FROM Win32_PerfRawData_PerfProc_Thread 
  WHERE IdProcess=4 and IdThread=8

実行中のプロセスに関する統計情報を表示するほとんどのプログラムは、WMIを使用してこの情報を照会するようになりました。

10
skamradt

特定の状況では、スレッドの実行時間が無意味になる可能性があることを知っておくことが重要です。マルチコアシステムの場合、通常、各スレッドの実行時間は15ミリ秒ごとに更新されるため、スレッドがこの時間より前にタスクを完了すると、ランタイムがリセットされます。詳細については、リンクを参照してください。 GetThreadTimes関数で、結果に驚きました!
および GetThreadTimesが間違っている理由

7
lsalamon

「GetThreadTimes」を使用していますか? 「GetThreadTimes」の呼び出し間の時間を測定し、前のユーザーやカーネルの時間を保存すると、最後にチェックしてからスレッドがどれだけの時間を持っているかがわかります。また、平均時間の経過時間もわかっているため、使用されたCPU時間を計算できます。 (タイマー解決の理由から)このチェックを1秒ごとに行い、その1秒間の平均CPU使用率を計算するのが最善です。

4
Goz

ここは単純なWMIクエリラッパーです。それの助けを借りて、あなたはデータを取得するためにこれを呼び出すことができます:

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

また、 Win32_PerfRawData_PerfProc_Thread のドキュメントを参照して、フェッチできる他のプロパティを確認することもできます。

1
Just Shadow

この例ではGetProcessTimes()を使用していますが、GetThreadTimes()を使用するように簡単に変更できます。

ISOCのclock()は、_CLOCKS_PER_SEC_で割ったときに経過したCPU秒数を示すと想定されています。ただし、Visual Studio2019のWebページには次のように記載されています。

クロック関数は、プロセス開始時のCRT初期化から経過した実時間を示します。この関数は、戻り値として正味CPU時間を指定するISOCに厳密には準拠していないことに注意してください。

だから、これが私が作った代役です。 Windows 7/Visual Studio 2017では、_CLOCKS_PER_SEC_は1000であり、GetProcessTimes()の精度はミリ秒しかないため、精度が失われることはなく、たとえばdoubleの代わりに_clock_t_が返されます。

_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;
}
_
1
Swiss Frank