如何通过.NET / C#查找CPU内核数?


317

是否可以通过.NET / C#找出CPU内核数?

PS:这是一个直接的代码问题,而不是“我应该使用多线程吗?” 题!:-)


7
您是否需要知道有多少个内核或有多少个逻辑处理器?对于仅运行多个线程,两者之一可能就足够了,但是在某些情况下,差异可能很重要。
凯文·凯布尔

有没有更新的方法可以做到这一点?
MoonKnight

Answers:


477

您可以获得与处理器有关的几种不同信息:

  1. 物理处理器数量
  2. 核心数
  3. 逻辑处理器数。

这些都可以不同。对于具有2个启用了双核超线程的处理器的计算机,有2个物理处理器,4个内核和8个逻辑处理器。

逻辑处理器的数量可通过Environment类获得,但其他信息仅可通过WMI获得(并且可能必须安装一些修补程序或Service Pack才能在某些系统上获得它):

确保在项目中添加对System.Management.dll的引用。 在.NET Core中,该引用作为NuGet包可用(仅适用于Windows)。

物理处理器:

foreach (var item in new System.Management.ManagementObjectSearcher("Select * from Win32_ComputerSystem").Get())
{
    Console.WriteLine("Number Of Physical Processors: {0} ", item["NumberOfProcessors"]);
}

核心数:

int coreCount = 0;
foreach (var item in new System.Management.ManagementObjectSearcher("Select * from Win32_Processor").Get())
{
    coreCount += int.Parse(item["NumberOfCores"].ToString());
}
Console.WriteLine("Number Of Cores: {0}", coreCount);

逻辑处理器:

Console.WriteLine("Number Of Logical Processors: {0}", Environment.ProcessorCount);

要么

foreach (var item in new System.Management.ManagementObjectSearcher("Select * from Win32_ComputerSystem").Get())
{
    Console.WriteLine("Number Of Logical Processors: {0}", item["NumberOfLogicalProcessors"]);
}

Windows排除的处理器:

您还可以在setupapi.dll中使用Windows API调用来发现已从Windows排除的处理器(例如,通过启动设置),并且使用上述方法无法检测到。下面的代码给出了已存在的逻辑处理器的总数(我无法弄清楚如何区分物理处理器与逻辑处理器),包括已从Windows中排除的逻辑处理器:

static void Main(string[] args)
{
    int deviceCount = 0;
    IntPtr deviceList = IntPtr.Zero;
    // GUID for processor classid
    Guid processorGuid = new Guid("{50127dc3-0f36-415e-a6cc-4cb3be910b65}");

    try
    {
        // get a list of all processor devices
        deviceList = SetupDiGetClassDevs(ref processorGuid, "ACPI", IntPtr.Zero, (int)DIGCF.PRESENT);
        // attempt to process each item in the list
        for (int deviceNumber = 0; ; deviceNumber++)
        {
            SP_DEVINFO_DATA deviceInfo = new SP_DEVINFO_DATA();
            deviceInfo.cbSize = Marshal.SizeOf(deviceInfo);

            // attempt to read the device info from the list, if this fails, we're at the end of the list
            if (!SetupDiEnumDeviceInfo(deviceList, deviceNumber, ref deviceInfo))
            {
                deviceCount = deviceNumber;
                break;
            }
        }
    }
    finally
    {
        if (deviceList != IntPtr.Zero) { SetupDiDestroyDeviceInfoList(deviceList); }
    }
    Console.WriteLine("Number of cores: {0}", deviceCount);
}

[DllImport("setupapi.dll", SetLastError = true)]
private static extern IntPtr SetupDiGetClassDevs(ref Guid ClassGuid,
    [MarshalAs(UnmanagedType.LPStr)]String enumerator,
    IntPtr hwndParent,
    Int32 Flags);

[DllImport("setupapi.dll", SetLastError = true)]
private static extern Int32 SetupDiDestroyDeviceInfoList(IntPtr DeviceInfoSet);

[DllImport("setupapi.dll", SetLastError = true)]
private static extern bool SetupDiEnumDeviceInfo(IntPtr DeviceInfoSet,
    Int32 MemberIndex,
    ref SP_DEVINFO_DATA DeviceInterfaceData);

[StructLayout(LayoutKind.Sequential)]
private struct SP_DEVINFO_DATA
{
    public int cbSize;
    public Guid ClassGuid;
    public uint DevInst;
    public IntPtr Reserved;
}

private enum DIGCF
{
    DEFAULT = 0x1,
    PRESENT = 0x2,
    ALLCLASSES = 0x4,
    PROFILE = 0x8,
    DEVICEINTERFACE = 0x10,
}

14
@StingyJack:是的,但我希望它的格式更好。当您必须构建原始字符串查询时,可发现性非常低。
凯文·凯布尔

5
WMI代码创建器将帮助进行值发现和查询创建(它甚至可以在c#/ vb.net中生成存根)。
StingyJack

4
在System.Management.dll中。您在项目中是否包含对该程序集的引用?
凯文·基布尔

2
上面的代码中存在次要问题。由于deviceCount是从零开始的,因此应该这样输出核心计数:Console.WriteLine("Number of cores: {0}", deviceCount + 1);
Francis Litterio 2013年

2
您不是通过不放置管理对象和搜索程序来引起问题吗?
本杰明

205
Environment.ProcessorCount

[文件]


12
这是如此简单,我几乎流下了眼泪。谢谢!
MrGreggles

70
这给出了逻辑处理器的数量,而不是内核的数量。
凯文·基布尔

8
@KevinKibler从问题上来说,我怀疑OP无法理解差异,如果您不知道差异,那可能就是您想要的。
Glenn Maynard 2014年

1
这还会在许多核心系统上返回错误的计数。我正在运行两个具有超线程的dodeca核心处理器,这使我总共有48个逻辑处理器。 Environment.ProcessorCount32.产生
艾伦克拉克科普兰JR

1
@AlexanderMorou,是的,这将无法在某些多CPU服务器上提供准确的结果。有一个修复程序,但尚未测试。
TheLegendaryCopyCoder

35

WMI查询的速度很慢,因此请尝试仅选择所需的成员,而不要使用Select *。

以下查询耗时3.4秒:

foreach (var item in new System.Management.ManagementObjectSearcher("Select * from Win32_Processor").Get())

虽然这个花费了0.122s:

foreach (var item in new System.Management.ManagementObjectSearcher("Select NumberOfCores from Win32_Processor").Get())

1
您在什么系统上运行它?我使用了多个“SELECT *”查询,并没有采取任何地方附近3.4秒,对数千台计算机,我的软件部署进行测试。我执行Select *,因为我从对象中获得了多个属性。但是,我做的有点不同:在Select *上创建一个ObjectQuery;获取ManagementObjectCollection; 然后在ManagementObjectCollection中foreach ManagementObject。
deegee

@deegee:您是对的,使用“ Select *”查询本身并不需要花费更长的时间,只是如果迭代所有返回的值而不是NumberOfCores,则下面的int解析很慢。
Aleix Mercader


10

看看.NET如何在内部至少说出这件事是一件很有趣的事情。它如下所示的“简单”:

namespace System.Threading
{
    using System;
    using System.Runtime.CompilerServices;

    internal static class PlatformHelper
    {
        private const int PROCESSOR_COUNT_REFRESH_INTERVAL_MS = 0x7530;
        private static volatile int s_lastProcessorCountRefreshTicks;
        private static volatile int s_processorCount;

        internal static bool IsSingleProcessor
        {
            get
            {
                return (ProcessorCount == 1);
            }
        }

        internal static int ProcessorCount
        {
            get
            {
                int tickCount = Environment.TickCount;
                int num2 = s_processorCount;
                if ((num2 == 0) || ((tickCount - s_lastProcessorCountRefreshTicks) >= 0x7530))
                {
                    s_processorCount = num2 = Environment.ProcessorCount;
                    s_lastProcessorCountRefreshTicks = tickCount;
                }
                return num2;
            }
        }
    }
}


4

从.NET Framework来源

您也可以在PInvoke上获得它Kernel32.dll

以下代码或多或少SystemInfo.cs来自System.Web源,位于此处

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct SYSTEM_INFO
{
  public ushort wProcessorArchitecture;
  public ushort wReserved;
  public uint dwPageSize;
  public IntPtr lpMinimumApplicationAddress;
  public IntPtr lpMaximumApplicationAddress;
  public IntPtr dwActiveProcessorMask;
  public uint dwNumberOfProcessors;
  public uint dwProcessorType;
  public uint dwAllocationGranularity;
  public ushort wProcessorLevel;
  public ushort wProcessorRevision;
}

internal static class SystemInfo 
{
    static int _trueNumberOfProcessors;
    internal static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);    

    [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
    internal static extern void GetSystemInfo(out SYSTEM_INFO si);

    [DllImport("kernel32.dll")]
    internal static extern int GetProcessAffinityMask(IntPtr handle, out IntPtr processAffinityMask, out IntPtr systemAffinityMask);

    internal static int GetNumProcessCPUs()
    {
      if (SystemInfo._trueNumberOfProcessors == 0)
      {
        SYSTEM_INFO si;
        GetSystemInfo(out si);
        if ((int) si.dwNumberOfProcessors == 1)
        {
          SystemInfo._trueNumberOfProcessors = 1;
        }
        else
        {
          IntPtr processAffinityMask;
          IntPtr systemAffinityMask;
          if (GetProcessAffinityMask(INVALID_HANDLE_VALUE, out processAffinityMask, out systemAffinityMask) == 0)
          {
            SystemInfo._trueNumberOfProcessors = 1;
          }
          else
          {
            int num1 = 0;
            if (IntPtr.Size == 4)
            {
              uint num2 = (uint) (int) processAffinityMask;
              while ((int) num2 != 0)
              {
                if (((int) num2 & 1) == 1)
                  ++num1;
                num2 >>= 1;
              }
            }
            else
            {
              ulong num2 = (ulong) (long) processAffinityMask;
              while ((long) num2 != 0L)
              {
                if (((long) num2 & 1L) == 1L)
                  ++num1;
                num2 >>= 1;
              }
            }
            SystemInfo._trueNumberOfProcessors = num1;
          }
        }
      }
      return SystemInfo._trueNumberOfProcessors;
    }
}

2
尝试过此操作,但它返回逻辑处理器的数量-与调用Environment.ProcessorCount的结果相同。
鲍勃·布赖恩

1

一种选择是从注册表中读取数据。有关该主题的MSDN文章:http : //msdn.microsoft.com/en-us/library/microsoft.win32.registry.localmachine (v= vs.71 ) .aspx

我相信处理器可以位于此处,HKEY_LOCAL_MACHINE \ HARDWARE \ DESCRIPTION \ System \ CentralProcessor

    private void determineNumberOfProcessCores()
    {
        RegistryKey rk = Registry.LocalMachine;
        String[] subKeys = rk.OpenSubKey("HARDWARE").OpenSubKey("DESCRIPTION").OpenSubKey("System").OpenSubKey("CentralProcessor").GetSubKeyNames();

        textBox1.Text = "Total number of cores:" + subKeys.Length.ToString();
    }

我有理由确定,大多数系统上都将存在注册表项。

尽管我会投入0.02美元。


这将提供Environment.ProcessorCount中已经可用的处理器数量,是否还有其他类似的方法来获取每个处理器的内核数量?
Armen'2

0

以下程序打印Windows机器的逻辑和物理核心。

#define STRICT
#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <omp.h>

template<typename T>
T *AdvanceBytes(T *p, SIZE_T cb)
{
 return reinterpret_cast<T*>(reinterpret_cast<BYTE *>(p) + cb);
}

class EnumLogicalProcessorInformation
{
public:
 EnumLogicalProcessorInformation(LOGICAL_PROCESSOR_RELATIONSHIP Relationship)
  : m_pinfoBase(nullptr), m_pinfoCurrent(nullptr), m_cbRemaining(0)
 {
  DWORD cb = 0;
  if (GetLogicalProcessorInformationEx(Relationship,
                                       nullptr, &cb)) return;
  if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) return;

  m_pinfoBase =
   reinterpret_cast<SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *>
                                     (LocalAlloc(LMEM_FIXED, cb));
  if (!m_pinfoBase) return;

  if (!GetLogicalProcessorInformationEx(Relationship, 
                                        m_pinfoBase, &cb)) return;

  m_pinfoCurrent = m_pinfoBase;
  m_cbRemaining = cb;
 }

 ~EnumLogicalProcessorInformation() { LocalFree(m_pinfoBase); }

 void MoveNext()
 {
  if (m_pinfoCurrent) {
   m_cbRemaining -= m_pinfoCurrent->Size;
   if (m_cbRemaining) {
    m_pinfoCurrent = AdvanceBytes(m_pinfoCurrent,
                                  m_pinfoCurrent->Size);
   } else {
    m_pinfoCurrent = nullptr;
   }
  }
 }

 SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *Current()
                                         { return m_pinfoCurrent; }
private:
 SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *m_pinfoBase;
 SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *m_pinfoCurrent;
 DWORD m_cbRemaining;
};


int __cdecl main(int argc, char **argv)
{
  int numLogicalCore = 0;
  int numPhysicalCore = 0;

  for (EnumLogicalProcessorInformation enumInfo(RelationProcessorCore);
      auto pinfo = enumInfo.Current(); enumInfo.MoveNext()) 
  {
      int numThreadPerCore = (pinfo->Processor.Flags == LTP_PC_SMT) ? 2 : 1;
      // std::cout << "thread per core: "<< numThreadPerCore << std::endl;
      numLogicalCore += numThreadPerCore;
      numPhysicalCore += 1;
  }

  printf ("Number of physical core = %d , Number of Logical core = %d \n", numPhysicalCore, numLogicalCore );

 char c = getchar(); /* just to wait on to see the results in the command prompt */
 return 0;
}

/*
I tested with Intel Xeon four cores with hyper threading and here is the result
Number of physical core = 4 , Number of Logical core = 8
*/

6
这个问题被标记为.NET。您的代码不是.NET代码。
围下李

-1

我一直在寻找相同的东西,但是我不想安装任何nuget或servicepack,所以我找到了这个解决方案,它非常简单直接,使用讨论,我认为运行WMIC命令非常容易并获取该值,这是C#代码。您只需要使用System.Management命名空间(以及用于流程的更多标准命名空间等等)。

string fileName = Path.Combine(Environment.SystemDirectory, "wbem", "wmic.exe");
string arguments = @"cpu get NumberOfCores";

Process process = new Process
{
    StartInfo =
    {
        FileName = fileName,
        Arguments = arguments,
        UseShellExecute = false,
        CreateNoWindow = true,
        RedirectStandardOutput = true,
        RedirectStandardError = true
    }
};

process.Start();

StreamReader output = process.StandardOutput;
Console.WriteLine(output.ReadToEnd());


process.WaitForExit();
int exitCode = process.ExitCode;
process.Close();

4
不知道为什么使简单的WMI查询变得如此复杂。确实没有必要将WMI命令行作为外部进程启动并解析其输出。.NET具有对WMI查询(System.Management.ManagementObjectSearcher)的内置支持,如此处其他答案所示。另外,我不知道为什么您认为使用.NET的内置WMI支持而不是wmic.exe时,需要使用nuget软件包或Service Pack ...
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.