如何通过线程计算CPU负载?

时间:2018-01-07 15:55:25

标签: delphi winapi

我想查看线程如何加载CPU,就像在ProcessExplorer中一样。 QueryThreadCycleTime可以检索线程CPU周期数,空闲周期数为QueryIdleProcessorCycleTime,但要了解繁忙周期,我必须使用QueryProcessCycleTime重复所有进程,我不会这样做; t认为非常有效率。是否有更简单的方法可以找到它?

1 个答案:

答案 0 :(得分:0)

  

但是要了解繁忙的周期,我必须使用QueryProcessCycleTime重申所有我认为不是非常有效的过程

足够有效。在我的计算机上需要大约15毫秒来枚举每个进程的每个线程并获取其循环时间。

这是一个快速的技巧,可以按所使用的CPU周期显示前20个线程:

var threads = new Dictionary<int, (DateTime at, ulong ctime, ulong perSec, string procName)>();
var seen = new HashSet<int>();
while (true)
{
    var cpuFreq = new ManagementObjectSearcher("select CurrentClockSpeed from Win32_Processor")
        .Get().Cast<ManagementObject>().Max(o => (uint) o.Properties["CurrentClockSpeed"].Value) * 1_000_000;
    seen.Clear();
    var start = DateTime.UtcNow;
    foreach (var p in Process.GetProcesses())
    {
        foreach (var t in p.Threads.Cast<ProcessThread>())
        {
            if (t.Id == 0)
                continue;
            seen.Add(t.Id);
            var thandle = OpenThread(ThreadAccess.QUERY_INFORMATION, false, (uint) t.Id);
            QueryThreadCycleTime(thandle, out ulong ctime);
            CloseHandle(thandle);
            var at = DateTime.UtcNow;
            if (!threads.ContainsKey(t.Id) || threads[t.Id].procName != p.ProcessName)
            {
                threads[t.Id] = (at: at, ctime: ctime, perSec: 0, procName: p.ProcessName);
            }
            else
            {
                var was = threads[t.Id];
                var diff = ctime >= was.ctime ? ctime - was.ctime : 0;
                threads[t.Id] = (at: at, ctime: ctime, perSec: (ulong) (diff / (at - was.at).TotalSeconds), procName: p.ProcessName);
            }
        }
    }
    foreach (var notSeen in threads.Keys.Where(id => !seen.Contains(id)).ToList())
        threads.Remove(notSeen);
    Console.Clear();
    Console.WriteLine($"Time taken to read thread counters: {(DateTime.UtcNow - start).TotalMilliseconds:0} ms");
    Console.WriteLine();
    Console.WriteLine($"Current CPU clock speed: {cpuFreq / 1_000_000:#,0} MHz");
    Console.WriteLine();
    foreach (var kvp in threads.OrderByDescending(x => x.Value.perSec).Take(20))
        Console.WriteLine($"{kvp.Value.perSec,15:#,0}   {kvp.Value.perSec / (double) cpuFreq * 100,3:0}%   {kvp.Value.procName + "/" + kvp.Key,-20}");
    Thread.Sleep(1000);
}

输出:

Time taken to read thread counters: 15 ms

Current CPU clock speed: 3,801 MHz

  1,125,936,851    30%   sqlservr/6292
    141,635,479     4%   sqlservr/6588
    106,651,766     3%   StatusScreenSite/7656
     80,182,644     2%   firefox/1880
     77,899,627     2%   ServiceHub.Host.CLR.x86/12668
     74,568,381     2%   ServiceHub.Host.CLR.x86/17304
     73,911,491     2%   AquaComputerService/14840
     72,843,646     2%   ServiceHub.Host.CLR.x86/13104
     64,164,832     2%   dwm/1404
     46,584,563     1%   firefox/14492
     46,538,943     1%   firefox/17924
     44,885,944     1%   sqlservr/12880
     41,697,624     1%   firefox/17164
     33,780,283     1%   AllThreadCpuUsage/10604    <------- this program
     33,165,365     1%   ProcessHacker/8588
     29,938,943     1%   TeamViewer/7356
     29,005,700     1%   OpenHardwareMonitor/11196
     28,808,683     1%   nvcontainer/3456
     28,536,182     1%   nvcontainer/5128
     28,294,193     1%   svchost/15732

%列显示单个内核而不是整个CPU的使用百分比。还请注意,MSDN警告我们不要将任何特定单位归因于线程周期计数返回的值,但实际上,在我的计算机上,它会返回实际的CPU周期。