Come trovare il numero di core della CPU tramite .NET / C #?


317

Esiste un modo tramite .NET / C # per scoprire il numero di core della CPU?

PS Questa è una domanda in codice diretto, non un "Devo usare il multi-threading?" domanda! :-)


7
Hai bisogno di sapere quanti core ci sono o quanti processori logici ci sono? Per l'esecuzione di più thread, probabilmente entrambi sono sufficienti, ma ci sono scenari in cui la differenza potrebbe essere importante.
Kevin Kibler,

C'è un modo più nuovo per farlo?
MoonKnight

Risposte:


477

Esistono diverse informazioni relative ai processori che è possibile ottenere:

  1. Numero di processori fisici
  2. Numero di core
  3. Numero di processori logici.

Questi possono essere tutti diversi; nel caso di una macchina con 2 processori dual-core abilitati all'hyper-threading, ci sono 2 processori fisici, 4 core e 8 processori logici.

Il numero di processori logici è disponibile tramite la classe Environment , ma le altre informazioni sono disponibili solo tramite WMI (e potrebbe essere necessario installare alcuni hotfix o service pack per ottenerlo su alcuni sistemi):

Assicurati di aggiungere un riferimento nel tuo progetto a System.Management.dll In .NET Core, questo è disponibile (solo per Windows) come pacchetto NuGet.

Processori fisici:

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

cores:

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);

Processori logici:

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

O

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

Processori esclusi da Windows:

È inoltre possibile utilizzare le chiamate API di Windows in setupapi.dll per rilevare processori che sono stati esclusi da Windows (ad esempio tramite le impostazioni di avvio) e che non sono rilevabili utilizzando i mezzi sopra indicati. Il codice seguente fornisce il numero totale di processori logici (non sono stato in grado di capire come differenziare i processori fisici da quelli logici) esistenti, inclusi quelli che sono stati esclusi da 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: Vero, ma vorrei che fosse in un formato migliore. La rilevabilità è piuttosto bassa quando è necessario creare query di stringa non elaborate.
Kevin Kibler,

5
WMI Code Creator ti aiuterà con la scoperta di valore e la creazione di query (può persino generare stub in c # / vb.net).
StingyJack,

4
È in System.Management.dll. Hai incluso un riferimento a quell'assemblaggio nel tuo progetto?
Kevin Kibler,

2
Minore problema fuori sede nel codice sopra. Poiché deviceCountè a base zero, il conteggio dei core dovrebbe essere prodotto in questo modo:Console.WriteLine("Number of cores: {0}", deviceCount + 1);
Francis Litterio,

2
Non stai causando problemi non eliminando gli oggetti di gestione e i ricercatori?
Benjamin,

205
Environment.ProcessorCount

[Documentazione]


12
È così meravigliosamente semplice che sto quasi versando lacrime. Grazie per la risposta!
MrGreggles,

70
Ciò fornisce il numero di processori logici, non il numero di core.
Kevin Kibler,

8
@KevinKibler Dalla domanda, sospetto che l'OP non capisca la differenza, e se non conosci la differenza, questo è probabilmente quello che vuoi.
Glenn Maynard,

1
Ciò restituisce anche un conteggio errato su molti sistemi core. Sto eseguendo due processori core dodeca con hyper-threading, che mi danno un totale di 48 processori logici. Environment.ProcessorCountproduce 32.
Allen Clark Copeland Jr

1
@AlexanderMorou, sì, questo non fornirà risultati accurati su alcuni server multi CPU. C'è una soluzione per questo, ma non l'ha ancora testato.
TheLegendaryCopyCoder il

35

Le query WMI sono lente, quindi prova a selezionare solo i membri desiderati invece di utilizzare Seleziona *.

La seguente query richiede 3.4s:

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

Mentre questo richiede 0.122s:

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

1
Su quale sistema stai eseguendo questo? Io uso più "SELECT *" query e non ci vuole nessuna parte vicino a 3,4 secondi, testati su migliaia di computer che il mio software è distribuito su. Faccio un Select * perché sto ottenendo più proprietà dall'oggetto. Tuttavia, lo faccio in modo leggermente diverso: creo un ObjectQuery su Select *; ottenere ManagementObjectCollection; quindi accedere a ManagementObject in ManagementObjectCollection.
deceduto l'

@deegee: hai ragione, la query stessa non impiega molto più tempo con "Seleziona *", è solo che l'analisi sotto riportata è lenta se iterando tutti i valori restituiti anziché solo NumberOfCores.
Aleix Mercader,


10

È piuttosto interessante vedere come .NET ottenga questo internamente per non dire altro ... È "semplice" come di seguito:

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

Dalla fonte .NET Framework

È anche possibile ottenere con PInvoke suKernel32.dll

Il seguente codice proviene più o meno SystemInfo.csdall'origine System.Web che si trova qui :

[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
Ho provato questo, ma restituisce il numero di processori logici, che è lo stesso risultato della chiamata Environment.ProcessorCount.
Bob Bryan

1

Un'opzione sarebbe quella di leggere i dati dal registro. Articolo MSDN sull'argomento: http://msdn.microsoft.com/en-us/library/microsoft.win32.registry.localmachine(v=vs.71).aspx )

Credo che i processori possano trovarsi qui, 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();
    }

Sono ragionevolmente sicuro che la voce di registro sarà presente sulla maggior parte dei sistemi.

Anche se vorrei buttare i miei $ 0,02 in.


Ciò fornirà il numero di processori già disponibili in Environment.ProcessorCount, esiste un altro modo simile per ottenere il numero di core per ciascun processore?
Armen,

0

Il seguente programma stampa i nuclei logici e fisici di una macchina 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
Questa domanda è taggata .NET; il tuo codice non è il codice .NET.
Wai Ha Lee,

-1

Stavo cercando la stessa cosa ma non voglio installare nessun nuget o servicepack, quindi ho trovato questa soluzione, è abbastanza semplice e diretta, usando questa discussione, ho pensato che sarebbe stato così semplice eseguire quel comando WMIC e ottieni quel valore, ecco il codice C #. Devi solo usare lo spazio dei nomi System.Management (e accoppiare più spazi dei nomi standard per il processo e così via).

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
Non sono sicuro del motivo per cui una semplice query WMI sia così complicata. Non è davvero necessario avviare la riga di comando WMI come processo esterno e analizzarne l'output. .NET ha il supporto integrato per le query WMI (System.Management.ManagementObjectSearcher), come alcune delle altre risposte qui già illustrate. Inoltre, non so perché pensi che sarebbero necessari pacchetti nuget o service pack quando si utilizza il supporto WMI incorporato di .NET invece di wmic.exe ...
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.