3

I want to retrieve the cpu usage of my system using c#. I have read that I must call the method NextValue() then wait for about a second and call the NextValue() method again. I want to do that asynchronous to not delay my UI thread so I have written following code

    public Task<float> GetCpuPerformanceAsync()
    {
        return Task.Run(() =>
        {
            CpuPerformance.NextValue();
            Task.Delay(1000);
            return CpuPerformance.NextValue();
        });
    }

This is the declaration of CpuPerformance

CpuPerformance = new PerformanceCounter("Processor Information", "% Processor Time", "_Total");

At the first time calling the asynchronous method as shown above it returns me the actual cpu usage but then calling it again after few secons it only shows 0 or 100 which doesn't coincide with the usage shown in my task manager

Can somebody help me solving this please?

Marcel Müller
  • 368
  • 3
  • 17
  • 1
    Does it actually delay? I'd expect to have to `await Task.Delay(1000)` ... – Fildor Jun 30 '20 at 12:03
  • 1
    Note that you're not awaiting your call to `Task.Delay`, so it won't actually wait at all. You probably want `public async Task GetCpuPerformanceAsync() { CpuPerformance.NextValue(); await Task.Delay(1000); return CpuPerformance.NextValue(); }`. That said, I'm not sure that calling `NextValue()`, throwing the result away, waiting, then calling it again will bring you any benefit – canton7 Jun 30 '20 at 12:03
  • 2
    _"I have read that I must ..."_ - can you give a reference where you read that? – Fildor Jun 30 '20 at 12:05
  • Thanks @canton7 your suggestions seems to work :-) Learned something new! – Marcel Müller Jun 30 '20 at 12:06
  • 1
    @Fildor Read that here: https://stackoverflow.com/questions/2181828/why-the-cpu-performance-counter-kept-reporting-0-cpu-usage – Marcel Müller Jun 30 '20 at 12:08
  • Ah, now it's clear. And what I was thinking. You don't have to do that _always_. If you create a new Performance Counter, it needs to "warm up". I.e. it first needs a value to compare to, so the output value is actually a valid value. So, if you keep that instance, just warm it up, then you can simply call `NextValue` without delay. – Fildor Jun 30 '20 at 12:12
  • @Fildor So in fact I need to delay the first time retrieving a value after the initialization of ```PerformanceCounter``` and then I just can call ```NextValue()``` without delaying? – Marcel Müller Jun 30 '20 at 12:17
  • @Fildor Cause I didn't understand what you mean with warm it up? Could you show me please what you mean? – Marcel Müller Jun 30 '20 at 12:29
  • I'll write up a short answer with what I think should work ... – Fildor Jun 30 '20 at 12:33

2 Answers2

5

This is an alternative to @Fildor's answer using the technique they came up with, but which avoids sleeping the thread the first time that CpuTimeInPercent is accessed.

public static class Helper
{
    private static readonly Task<PerformanceCounter> performanceCounter = GetCounter();

    private static async Task<PerformanceCounter> GetCounter()
    {
        PerformanceCounter pc = new PerformanceCounter("Processor Information", "% Processor Time", "_Total");
        // "warm up"
        pc.NextValue();
        await Task.Delay(1000);
        // return ready-to-use instance
        return pc;
    }

    public static async Task<float> GetCpuTimeInPercentAsync()
    {
        var counter = await performanceCounter;
        return counter.NextValue();
    }
}

This will start executing GetCounter() at some point before performanceCounter is executed for the first time (it might be when the assembly is loaded, it might be when GetCounter() is called, the runtime has a lot of freedom here). If you call GetCpuTimeInPercentAsync while it's doing the warm-up, then it will wait until it's finished and then get the CPU usage. If you call it subsequently, or after it's finished the warm-up, then it will return the CPU usage straight away.

canton7
  • 37,633
  • 3
  • 64
  • 77
  • 1
    Excellent! I'll leave mine anyway, if just to show the usage of `Lazy` ... – Fildor Jun 30 '20 at 13:12
  • 2
    @Fildor Please do -- I think it's right that OP accepts your answer, as you did the legwork. I just wanted to show how it could be done with tasks, and it's a bit too long for a comment. – canton7 Jun 30 '20 at 13:13
3

Unfortunately, dotnetfiddle won't allow me PerformanceCounters, but this should work anyway:

public class Helper // Of course, you do not necessarily need an extra class ...
{
    // Lazy Pattern: Will create an instance on first usage.
    private static Lazy<PerformanceCounter> CpuPerformance = new Lazy<PerformanceCounter>(() => {
        // this will be used as factory to create the instance ...
        // 1. create counter
        PerformanceCounter pc = new PerformanceCounter("Processor Information", "% Processor Time", "_Total");
        // "warm up"
        pc.NextValue();
        Thread.Sleep(1000);
        // return ready-to-use instance
        return pc;
        // ^^ this will be executed only _once_.
    });

    // Read-Only Property uses Lazy to ensure instance exists, then use it.
    public static float CpuTimeInPercent {
        get { return CpuPerformance.Value.NextValue(); }
    }
}

Usage:

Console.WriteLine("Performance: {0}%", Helper.CpuTimeInPercent);
Fildor
  • 14,510
  • 4
  • 35
  • 67
  • 3
    @MarcelMüller note that this will pause your thread for a second the first time it accesses `CpuTimeInPercent`, which you were presumably trying to avoid in your question – canton7 Jun 30 '20 at 13:05
  • 1
    I guess you can tweak this to not block the UI Thread on creation. This was just inteded to show what the Source meant by what I referred to as "warm up". @canton7 – Fildor Jun 30 '20 at 13:09