草庐IT

C# 几种获取电脑内存、CPU信息的方案

唐宋元明清的博客 2023-03-28 原文

计数器、WMI

获取设备的内存信息,如系统可用运行内存:

 1     public static async Task<double> GetMemoryAvailableAsync(FileSizeUnit fileSizeUnit = FileSizeUnit.GB)
 2     {
 3         return await Task.Run(() =>
 4         {
 5             using var managementClass = new ManagementClass("Win32_PerfFormattedData_PerfOS_Memory");
 6             using var instances = managementClass.GetInstances();
 7             double available = 0;
 8             foreach (var mo in instances)
 9             {
10                 //AvailableMBytes单位是MB
11                 var size = long.Parse(mo.Properties["AvailableMBytes"].Value.ToString()) * 1024 * 1024;
12                 available += size.ConvertTo(fileSizeUnit);
13             }
14 
15             return available;
16         });
17     }

以上是ManagementClass方式实现,还有ManagementObjectSearcher,都是WMI检索查询。

WMI查询比较慢,上面一段耗时在200ms+。

还有一种用的较多的,PerformanceCounter性能计数器,以CPU获取为例:

 1     public static async Task<double> GetUsageByCounterAsync()
 2     {
 3         //CPU计数器 
 4         using var pcCpuUsage = new PerformanceCounter("Processor", "% Processor Time", "_Total") { MachineName = "." };
 5         // NextValue首次会返回0,所以需要加个延时下次再获取值
 6         pcCpuUsage.NextValue();
 7         await Task.Delay(TimeSpan.FromMilliseconds(500));
 8         var cpuUsed = pcCpuUsage.NextValue();
 9         return cpuUsed;
10     }

性能计数器,也有一定的耗时40ms以上。另外因为它实现方式,初始化后无法第一次获取到真正数值,需要间隔一段时间再去拿。所以此方案实际耗时挺高

WMI、性能计数器,昨天遇到了使用异常:

 

看源码,计数器是注册表PerformanceData位置损坏了,而Management是IWbemServices获取状态码ErrorCode异常。

PerformanceCounter是WMI,而WMI是基于WBEM协议实现的,所以我理解成上面的异常其实是一类问题。

官网有对此类异常有一些描述:重新生成性能计数器库值 - Windows Server | Microsoft Learn 

所以基于PerformanceCounter、ManagementClass以及ManagementObjectSearcher的实现,有一定风险。

kernel32

kernel32下有个函数可以获取内存状态

1     [DllImport("kernel32.dll")]
2     [return: MarshalAs(UnmanagedType.Bool)]
3     static extern bool GlobalMemoryStatusEx(ref MEMORYINFO mi);

以下是获取可用运行内存的实现:

    //Define the information structure of memory
    [StructLayout(LayoutKind.Sequential)]
    struct MEMORYINFO
    {
        public uint dwLength; //Current structure size
        public uint dwMemoryLoad; //Current memory utilization
        public ulong ullTotalPhys; //Total physical memory size
        public ulong ullAvailPhys; //Available physical memory size
        public ulong ullTotalPageFile; //Total Exchange File Size
        public ulong ullAvailPageFile; //Total Exchange File Size
        public ulong ullTotalVirtual; //Total virtual memory size
        public ulong ullAvailVirtual; //Available virtual memory size
        public ulong ullAvailExtendedVirtual; //Keep this value always zero
    }

    /// <summary>
    /// Get the current memory usage
    /// </summary>
    /// <returns></returns>
    private static MEMORYINFO GetMemoryStatus()
    {
        MEMORYINFO memoryInfo = new MEMORYINFO();
        memoryInfo.dwLength = (uint)System.Runtime.InteropServices.Marshal.SizeOf(memoryInfo);
        GlobalMemoryStatusEx(ref memoryInfo);
        return memoryInfo;
    }
    /// <summary>
    /// 获取系统可用运行内存
    /// </summary>
    /// <param name="fileSizeUnit">默认单位GB</param>
    /// <returns></returns>
    public static double GetMemoryAvailable(FileSizeUnit fileSizeUnit = FileSizeUnit.GB)
    {
        var memoryStatus = GetMemoryStatus();
        var memoryAvailable = ((long)memoryStatus.ullAvailPhys).ConvertTo(fileSizeUnit);
        return memoryAvailable;
    }

上述方式,获取速度超快,几乎不耗时。

通过Kernel32-GetSystemTimes方式,获取CPU信息(CPU比例计算逻辑,代码略多点):

 1     /// <summary>
 2     /// 获取CPU占用率/使用率(单位:%)
 3     /// </summary>
 4     /// <returns></returns>
 5     public static async Task<double> GetUsageByKernelAsync()
 6     {
 7         long idleTime1 = 0;
 8         long kernelTime1 = 0;
 9         long userTime1 = 0;
10         if (GetSystemTimes(out var lpIdleTime, out var lpKernelTime, out var lpUserTime))
11         {
12             idleTime1 = lpIdleTime;
13             kernelTime1 = lpKernelTime;
14             userTime1 = lpUserTime;
15         }
16         //添加俩次获取CPU信息的间隔
17         await Task.Delay(TimeSpan.FromSeconds(0.5));
18         long idleTime2 = 0;
19         long kernelTime2 = 0;
20         long userTime2 = 0;
21         if (GetSystemTimes(out var lpIdleTime2, out var lpKernelTime2, out var lpUserTime2))
22         {
23             idleTime2 = lpIdleTime2;
24             kernelTime2 = lpKernelTime2;
25             userTime2 = lpUserTime2;
26         }
27         //分别获取到用户时间、内核时间、空闲时间
28         var userTime = userTime2 - userTime1;
29         var kernelTime = kernelTime2 - kernelTime1;
30         var idleTime = idleTime2 - idleTime1;
31         //计算Cpu占用率。计算公式:用户时间+内核时间-空闲时间/用户时间+内核时间
32         var systemTotal = kernelTime + userTime;
33         var cpu = (systemTotal - idleTime) * 10000 / systemTotal;
34         return cpu / 100.0;
35     }
36 
37     /// <summary>
38     /// 获取系统CPU时间数据
39     /// </summary>
40     /// <param name="lpIdleTime">空闲时间</param>
41     /// <param name="lpKernelTime">内核时间</param>
42     /// <param name="lpUserTime">用户时间</param>
43     /// <returns></returns>
44     [DllImport("kernel32.dll", SetLastError = true)]
45     static extern bool GetSystemTimes(out long lpIdleTime, out long lpKernelTime, out long lpUserTime);

另外,也有一种途径可以获取到内存信息,引用程序集Microsoft.VisualBasic,Microsoft.VisualBasic.Devices下有个ComputerInfo类

var physicalMemory = new Microsoft.VisualBasic.Devices.ComputerInfo().AvailablePhysicalMemory;

可以拿到可用内存、总内存,不过CPU信息是没有的。

ComputerInfo的内部源码,我标注了下:

所以ComputerInfo,也是基于GlobalMemoryStatusEx函数做了封装,大家可以直接用。

 

关键字:【Kernek32-GlobalMemoryStatusEx】【Kernek32-GetSystemTimes】

参考列表:

How to get system specs (processor, memory etc...) in C#? (microsoft.com)
Get the CPU Usage in C# | Delft Stack
C#获取CPU和内存使用率 - 雨水的命运 - 博客园 (cnblogs.com)
Determine CPU usage of current process (C++ and C#) | Philosophical Geek
Get CPU Usage with GetSystemTimes - CodeProject

 

有关C# 几种获取电脑内存、CPU信息的方案的更多相关文章

  1. ruby-on-rails - Ruby net/ldap 模块中的内存泄漏 - 2

    作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代

  2. ruby-on-rails - Rails 常用字符串(用于通知和错误信息等) - 2

    大约一年前,我决定确保每个包含非唯一文本的Flash通知都将从模块中的方法中获取文本。我这样做的最初原因是为了避免一遍又一遍地输入相同的字符串。如果我想更改措辞,我可以在一个地方轻松完成,而且一遍又一遍地重复同一件事而出现拼写错误的可能性也会降低。我最终得到的是这样的:moduleMessagesdefformat_error_messages(errors)errors.map{|attribute,message|"Error:#{attribute.to_s.titleize}#{message}."}enddeferror_message_could_not_find(obje

  3. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  4. ruby - 在 jRuby 中使用 'fork' 生成进程的替代方案? - 2

    在MRIRuby中我可以这样做:deftransferinternal_server=self.init_serverpid=forkdointernal_server.runend#Maketheserverprocessrunindependently.Process.detach(pid)internal_client=self.init_client#Dootherstuffwithconnectingtointernal_server...internal_client.post('somedata')ensure#KillserverProcess.kill('KILL',

  5. ruby - 简单获取法拉第超时 - 2

    有没有办法在这个简单的get方法中添加超时选项?我正在使用法拉第3.3。Faraday.get(url)四处寻找,我只能先发起连接后应用超时选项,然后应用超时选项。或者有什么简单的方法?这就是我现在正在做的:conn=Faraday.newresponse=conn.getdo|req|req.urlurlreq.options.timeout=2#2secondsend 最佳答案 试试这个:conn=Faraday.newdo|conn|conn.options.timeout=20endresponse=conn.get(url

  6. ruby-on-rails - Ruby 中的内存模型 - 2

    ruby如何管理内存。例如:如果我们在执行过程中采用C程序,则以下是内存模型。类似于这个ruby如何处理内存。C:__________________|||stack|||------------------||||------------------|||||Heap|||||__________________|||data|__________________|text|__________________Ruby:? 最佳答案 Ruby中没有“内存”这样的东西。Class#allocate分配一个对象并返回该对象。这就是程序

  7. ruby - 从 Ruby 中的主机名获取 IP 地址 - 2

    我有一个存储主机名的Ruby数组server_names。如果我打印出来,它看起来像这样:["hostname.abc.com","hostname2.abc.com","hostname3.abc.com"]相当标准。我想要做的是获取这些服务器的IP(可能将它们存储在另一个变量中)。看起来IPSocket类可以做到这一点,但我不确定如何使用IPSocket类遍历它。如果它只是尝试像这样打印出IP:server_names.eachdo|name|IPSocket::getaddress(name)pnameend它提示我没有提供服务器名称。这是语法问题还是我没有正确使用类?输出:ge

  8. ruby - 获取模块中定义的所有常量的值 - 2

    我想获取模块中定义的所有常量的值:moduleLettersA='apple'.freezeB='boy'.freezeendconstants给了我常量的名字:Letters.constants(false)#=>[:A,:B]如何获取它们的值的数组,即["apple","boy"]? 最佳答案 为了做到这一点,请使用mapLetters.constants(false).map&Letters.method(:const_get)这将返回["a","b"]第二种方式:Letters.constants(false).map{|c

  9. ruby-on-rails - 获取 inf-ruby 以使用 ruby​​ 版本管理器 (rvm) - 2

    我安装了ruby​​版本管理器,并将RVM安装的ruby​​实现设置为默认值,这样'哪个ruby'显示'~/.rvm/ruby-1.8.6-p383/bin/ruby'但是当我在emacs中打开inf-ruby缓冲区时,它使用安装在/usr/bin中的ruby​​。有没有办法让emacs像shell一样尊重ruby​​的路径?谢谢! 最佳答案 我创建了一个emacs扩展来将rvm集成到emacs中。如果您有兴趣,可以在这里获取:http://github.com/senny/rvm.el

  10. c# - 如何在 ruby​​ 中调用 C# dll? - 2

    如何在ruby​​中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL

随机推荐