You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

读取/proc/stat计算CPU使用率触发DivideByZeroException问题求助

Fixing DivideByZeroException and Inaccurate CPU Usage Calculation on Linux (Raspberry Pi)

Let's break down the issues in your code and fix them step by step:

1. Asynchronous Delay Issue Causing Zero Total Difference

Your current code uses Task.Delay(200) followed by a recursive call to GetCPUUsage(), but Task.Delay is asynchronous—it doesn't block the current thread. This means the recursive call runs immediately after starting the delay, so you're reading the /proc/stat data twice in rapid succession. The CPU stats won't change in that tiny window, leading to totalDifference = 0 and the divide-by-zero error.

Fix: Make GetCPUUsage an asynchronous method, await the delay, and avoid recursion. Also, ensure you only read the stats once per sample interval.

2. Incorrect CPU Usage Formula

Your calculation line has a syntax error in parentheses, which would lead to wrong values even when there's no divide-by-zero:

decimal cpuPercentage = (totalDifference - idleDifference / totalDifference) * 100;

The correct formula (from the logic you referenced) is:

decimal cpuPercentage = ((totalDifference - idleDifference) / totalDifference) * 100;

You need to calculate the difference between total and idle first, then divide by the total difference.

3. Flawed /proc/stat Parsing Logic

Your SaveIntsFromFilePath method has a few critical issues:

  • The i > 5 check is arbitrary and might skip the first set of CPU stats (the first line starts with cpu , so the first number starts at index 4).
  • It doesn't handle consecutive whitespace (common in /proc/stat lines), which would result in empty strings being parsed (throwing exceptions) or missing values.
  • You're appending to an existing list without clearing it first, so subsequent calls will keep adding more data, leading to incorrect index references (like oldCpuStatistics[3] pointing to wrong values).

Fix: Simplify parsing by splitting the line into tokens, filtering out non-numeric values, and replacing the list instead of appending.

4. Missing Reset of Statistics Lists

After calculating the CPU usage, you don't update oldCpuStatistics with the new values, and you don't clear newCpuStatistics. This means subsequent runs will use stale data, leading to incorrect calculations or exceptions.


Corrected Code

Here's the revised version of your class with all fixes applied:

public class HardwareInfoManager : IHardwareInfoManager
{
    private readonly IConfiguration _configuration;
    private List<long> _oldCpuStats;

    public HardwareInfoManager(IConfiguration configuration)
    {
        _configuration = configuration;
        _oldCpuStats = new List<long>();
    }

    public async Task<decimal> GetCPUUsageAsync()
    {
        string cpuStatPath = "/proc/stat";
        if (!File.Exists(cpuStatPath))
            return 0;

        // First run: initialize old stats and wait for interval
        if (_oldCpuStats.Count == 0)
        {
            _oldCpuStats = ParseCpuStats(cpuStatPath);
            await Task.Delay(200); // Wait for stats to change
            return await GetCPUUsageAsync();
        }

        // Get new stats
        var newCpuStats = ParseCpuStats(cpuStatPath);

        // Validate we have enough data points
        if (newCpuStats.Count < 8)
            return 0;

        // Calculate idle and non-idle times
        long prevIdle = _oldCpuStats[3] + _oldCpuStats[4];
        long idle = newCpuStats[3] + newCpuStats[4];

        long prevNonIdle = _oldCpuStats[0] + _oldCpuStats[1] + _oldCpuStats[2] + 
                           _oldCpuStats[5] + _oldCpuStats[6] + _oldCpuStats[7];
        long nonIdle = newCpuStats[0] + newCpuStats[1] + newCpuStats[2] + 
                       newCpuStats[5] + newCpuStats[6] + newCpuStats[7];

        long prevTotal = prevIdle + prevNonIdle;
        long total = idle + nonIdle;

        long totalDiff = total - prevTotal;
        long idleDiff = idle - prevIdle;

        // Avoid divide by zero
        if (totalDiff == 0)
            return 0;

        decimal cpuPercentage = ((totalDiff - idleDiff) / (decimal)totalDiff) * 100;
        cpuPercentage = Math.Round(cpuPercentage, 2);

        // Update old stats for next run
        _oldCpuStats = newCpuStats;

        return cpuPercentage;
    }

    private List<long> ParseCpuStats(string path)
    {
        var cpuLine = File.ReadAllLines(path).FirstOrDefault(line => line.StartsWith("cpu "));
        if (string.IsNullOrEmpty(cpuLine))
            return new List<long>();

        // Split into tokens, filter out non-numeric, parse to long
        return cpuLine.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)
                      .Skip(1) // Skip the "cpu" prefix
                      .Where(token => long.TryParse(token, out _))
                      .Select(long.Parse)
                      .ToList();
    }
}

Additional Recommendations

  • Avoid Recursion: The recursive call in the original code could lead to stack overflow if called repeatedly. The async approach with await is safer.
  • Add Error Handling: Wrap file operations in try-catch blocks to handle cases where /proc/stat is temporarily unavailable (unlikely on Linux, but possible in edge cases).
  • Validate Data: Always check that the parsed stats list has enough elements before accessing indexes to prevent IndexOutOfRangeException.
  • Use readonly Fields: Mark fields like _configuration and _oldCpuStats as readonly where appropriate for better immutability.

内容的提问来源于stack exchange,提问作者grozdeto

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 09:27:30