CPU friendly infinite loop
Asked Answered
E

12

63

Writing an infinite loop is simple:

while(true){
    //add whatever break condition here
}

But this will trash the CPU performance. This execution thread will take as much as possible from CPU's power.

What is the best way to lower the impact on CPU? Adding some Thread.Sleep(n) should do the trick, but setting a high timeout value for Sleep() method may indicate an unresponsive application to the operating system.

Let's say I need to perform a task each minute or so in a console app. I need to keep Main() running in an "infinite loop" while a timer will fire the event that will do the job. I would like to keep Main() with the lowest impact on CPU.

What methods do you suggest. Sleep() can be ok, but as I already mentioned, this might indicate an unresponsive thread to the operating system.

LATER EDIT:

I want to explain better what I am looking for:

  1. I need a console app not Windows service. Console apps can simulate the Windows services on Windows Mobile 6.x systems with Compact Framework.

  2. I need a way to keep the app alive as long as the Windows Mobile device is running.

  3. We all know that the console app runs as long as its static Main() function runs, so I need a way to prevent Main() function exit.

  4. In special situations (like: updating the app), I need to request the app to stop, so I need to infinitely loop and test for some exit condition. For example, this is why Console.ReadLine() is no use for me. There is no exit condition check.

  5. Regarding the above, I still want Main() function as resource friendly as possible. Let asside the fingerprint of the function that checks for the exit condition.

Exothermic answered 13/9, 2011 at 12:50 Comment(8)
Thread.Sleep(0) is probably goodFib
Why don't you a Timer?Norry
@Fib Sleep(0) relinquishes the remainder of the current time-slice, but it still leads to 100% CPU utilization. So it doesn't work here.Iz
Can you elaborate on the "may indicate an unresponsive application to the operating system" problem? Are you sure that this is an issue in a console application?Loutish
Also the OS doesn't care about unresponsive threads. It cares about unresponsive threads that own a window. And that doesn't seem to be the case here.Iz
@CodeInChaos - Sleep(0) results in 'whatever % isn't being used elsewhere' which is sometimes pretty cool - especially when making an application that uses 'remaining resources' to do its work (e.g. SETI@Home or something would be a use case).Rescissory
For SETI and similar applications I'd simply reduce the priority of my process/thread so it doesn't get CPU time when somebody else needs it.Iz
What does your Main method need to do in this "infinite loop"? Perhaps main can kick off the timer and then call Console.ReadLine().Comfortable
B
61

To avoid the infinity loop simply use a WaitHandle. To let the process be exited from the outer world use a EventWaitHandle with a unique string. Below is an example.

If you start it the first time, it simple prints out a message every 10 seconds. If you start in the mean time a second instance of the program it will inform the other process to gracefully exit and exits itself also immediately. The CPU usage for this approach: 0%

private static void Main(string[] args)
{
    // Create a IPC wait handle with a unique identifier.
    bool createdNew;
    var waitHandle = new EventWaitHandle(false, EventResetMode.AutoReset, "CF2D4313-33DE-489D-9721-6AFF69841DEA", out createdNew);
    var signaled = false;

    // If the handle was already there, inform the other process to exit itself.
    // Afterwards we'll also die.
    if (!createdNew)
    {
        Log("Inform other process to stop.");
        waitHandle.Set();
        Log("Informer exited.");

        return;
    }

    // Start a another thread that does something every 10 seconds.
    var timer = new Timer(OnTimerElapsed, null, TimeSpan.Zero, TimeSpan.FromSeconds(10));

    // Wait if someone tells us to die or do every five seconds something else.
    do
    {
        signaled = waitHandle.WaitOne(TimeSpan.FromSeconds(5));
        // ToDo: Something else if desired.
    } while (!signaled);

    // The above loop with an interceptor could also be replaced by an endless waiter
    //waitHandle.WaitOne();

    Log("Got signal to kill myself.");
}

private static void Log(string message)
{
    Console.WriteLine(DateTime.Now + ": " + message);
}

private static void OnTimerElapsed(object state)
{
    Log("Timer elapsed.");
}
Barnacle answered 11/9, 2012 at 10:40 Comment(7)
This is a much, much better answer than most in this thread, I went from ~30% CPU usage with a while(true) style loop to averaging ~0.04% CPU usage with a WaitHandle.Curiel
how to exit loop by event and then start new one?Gaseous
@AntoshaShmonoff: To exit the loop you have to call waitHandle.Set() from a different thread or process, then recreate the handle with new EventWaitHandle(...) and doing the while loop again.Barnacle
i just dont understand how to use this line var timer = new Timer(OnTimerElapsed, null, TimeSpan.Zero, TimeSpan.FromSeconds(10));Alrick
It creates a System.Threading.Timer. The parameters are the delegate that should be called, the parameters of the method, how long to wait till the first call and how much time between all later calls should be.Barnacle
Oliver, would you give a different answer to this today with async/await and newer constructs? There is even a new async-ready timer API now, correct?Stichomythia
I don't think so. async/await is to optimize parallel I/O bound calls and not to optimize parallel CPU bound calls. Potentially within the other thread that does something it could be useful to use async/await, but not here in the outer loop.Barnacle
E
9

You can use System.Threading.Timer Class which provides ability to execute callback asynchronously in a given period of time.

public Timer(
    TimerCallback callback,
    Object state,
    int dueTime,
    int period
)

As alternative there is System.Timers.Timer class which exposes Elapsed Event which raises when a given period of time is elapsed.

Embroidery answered 13/9, 2011 at 12:52 Comment(10)
Time does have a minimum resolution of 30ms - which may or may not be an issue for him; at least indicate it in your answer.Rescissory
@Jonathan Dickinson : I believe from CPU load perspectives such discrete value is pretty enough in opposite to 100% CPU load, but anyway thanks for thsi detailEmbroidery
@Jonathan Dickinson : btw, where is you found this value?Embroidery
it should be common knowledge if you are doing threading (as it affects everything: Environment.TickCount, Thread.Sleep, etc.) - and, oops, it's 15.6ms: #3744532Rescissory
Yep but why 30ms rather than ~15ms? Are any references to MSDN?Embroidery
I read up on this stuff years ago, and back then the BIOS clocks might have been at 33hz. So basically: 'it's probably ~15ms but can be as bad as 30ms'. You can't pin it on an exact value because it's hardware/BIOS dependant (you need to call timeGetDevCaps to find out the frequency) - printing out Environment.TickCount on my machine has a resolution of 20~30ms. msdn.microsoft.com/en-us/library/ms686298(v=vs.85).aspxRescissory
let us continue this discussion in chatRescissory
@All At least in my case, timer resolution is not an issue. I want something to be done once in "approximatevely" 1 minute. But take in consideration 1 hour or 1 day or 1 week. So why should I keep the main thread busy to test an always true condition like while(true) instead of putting the thread to sleep for the required amount of time?Exothermic
@Embroidery It's not a timer issue but rather an issue of how to keep Main() function running without "eating" CPU time and waiting for different other timers to trigger. I also would like to exit the app (that is, Main() function) if certain condition is met (like: a socket command was received, etc).Exothermic
@Ai: you can use ManualResetEvent + WaitOne() for such scenarioEmbroidery
D
1

Why would you condone the use of an infinite loop? For this example would setting the program up as a scheduled task, to be run every minute, not be more economical?

Downpour answered 13/9, 2011 at 12:53 Comment(2)
I think the overhead of starting a new instance of a .net program every minute is a bit high.Iz
There could be a bit more overhead, but I would still shy away from an infinite loop or a service. You could get the best of both worlds and run the task every 5 mins, but the task has a Timer which triggers the action every minute for 5 mins. This means less overhead, from starting the .Net program every minute, and less chance of a memory leak, which could lock the CPU and affect other processes and/or users.Downpour
S
1

Why don't you write a small application and use the system's task scheduler to run it every minute, hour...etc?

Another option would be to write a Windows Service which runs in the background. The service could use a simple Alarm class like the following on MSDN:

http://msdn.microsoft.com/en-us/library/wkzf914z%28v=VS.90%29.aspx#Y2400

You can use it to periodically trigger your method. Internally this Alarm class uses a timer:

http://msdn.microsoft.com/en-us/library/system.timers.timer.aspx

Just set the timer's interval correctly (e.g. 60000 milliseconds) and it will raise the Elapsed event periodically. Attach an event handler to the Elapsed event to perform your task. No need to implement an "infinite loop" just to keep the application alive. This is handled for you by the service.

Sonni answered 13/9, 2011 at 12:57 Comment(3)
Task Scheduler can only run every 5 minutes at a minimum by the looks of things..Choiseul
More a fan of using Quartz.NET lately as a way of scheduling tasks. Creating a Windows Service using Topshelf and scheduling jobs via Quartz.NET.Sonni
Wish I'd seen Quartz.Net before I started my recent project.. On paper it looks awesome..Choiseul
O
1

I did this for an application that had to process files as they were dropped on a folder. Your best bet is a timer (as suggested) with a Console.ReadLine() at the end of "main" without putting in a loop.

Now, your concern about telling the app to stop:

I have also done this via some rudimentary "file" monitor. Simply creating the file "quit.txt" in the root folder of the application (by either my program or another application that might request it to stop) will make the application quit. Semi-code:

<do your timer thing here>
watcher = new FileSystemWatcher();
watcher.Path = <path of your application or other known accessible path>;
watcher.Changed += new FileSystemEventHandler(OnNewFile);
Console.ReadLine();

The OnNewFile could be something like this:

private static void OnNewFile(object source, FileSystemEventArgs e)
{
    if(System.IO.Path.GetFileName(e.FullPath)).ToLower()=="quit.txt")
        ... remove current quit.txt
        Environment.Exit(1);
}

Now you mentioned that this is (or could be) for a mobile application? You might not have the file system watcher. In that case, maybe you just need to "kill" the process (you said "In special situations (like: updating the app), I need to request the app to stop". Whoever the "requester" to stop it is, should simply kill the process)

Orlene answered 18/5, 2012 at 20:1 Comment(0)
N
1

It sounds to me like you want Main() to enter an interruptable loop. For this to happen, multiple threads must be involved somewhere (or your loop must poll periodically; I am not discussing that solution here though). Either another thread in the same application, or a thread in another process, must be able to signal to your Main() loop that it should terminate.

If this is true, then I think you want to use a ManualResetEvent or an EventWaitHandle . You can wait on that event until it is signalled (and the signalling would have to be done by another thread).

For example:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace Demo
{
    class Program
    {
        static void Main(string[] args)
        {
            startThreadThatSignalsTerminatorAfterSomeTime();
            Console.WriteLine("Waiting for terminator to be signalled.");
            waitForTerminatorToBeSignalled();
            Console.WriteLine("Finished waiting.");
            Console.ReadLine();
        }

        private static void waitForTerminatorToBeSignalled()
        {
            _terminator.WaitOne(); // Waits forever, but you can specify a timeout if needed.
        }

        private static void startThreadThatSignalsTerminatorAfterSomeTime()
        {
            // Instead of this thread signalling the event, a thread in a completely
            // different process could do so.

            Task.Factory.StartNew(() =>
            {
                Thread.Sleep(5000);
                _terminator.Set();
            });
        }

        // I'm using an EventWaitHandle rather than a ManualResetEvent because that can be named and therefore
        // used by threads in a different process. For intra-process use you can use a ManualResetEvent, which 
        // uses slightly fewer resources and so may be a better choice.

        static readonly EventWaitHandle _terminator = new EventWaitHandle(false, EventResetMode.ManualReset, "MyEventName");
    }
}
Neurogenic answered 11/9, 2012 at 9:31 Comment(0)
R
0

You can use Begin-/End-Invoke to yield to other threads. E.g.

public static void ExecuteAsyncLoop(Func<bool> loopBody)
{
    loopBody.BeginInvoke(ExecuteAsyncLoop, loopBody);
}

private static void ExecuteAsyncLoop(IAsyncResult result)
{
    var func = ((Func<bool>)result.AsyncState);
    try
    {
        if (!func.EndInvoke(result))
            return;
    }
    catch
    {
        // Do something with exception.
        return;
    }

    func.BeginInvoke(ExecuteAsyncLoop, func);
}

You would use it as such:

ExecuteAsyncLoop(() =>
    {
        // Do something.
        return true; // Loop indefinitely.
    });

This used 60% of one core on my machine (completely empty loop). Alternatively, you can use this (Source) code in the body of your loop:

private static readonly bool IsSingleCpuMachine = (Environment.ProcessorCount == 1);
[DllImport("kernel32", ExactSpelling = true)]
private static extern void SwitchToThread();

private static void StallThread()
{
    // On a single-CPU system, spinning does no good
    if (IsSingleCpuMachine) SwitchToThread();
    // Multi-CPU system might be hyper-threaded, let other thread run
    else Thread.SpinWait(1);
}

while (true)
{
    // Do something.
    StallThread();
}

That used 20% of one core on my machine.

Rescissory answered 13/9, 2011 at 13:14 Comment(2)
I believe you do not need to check whether current machine is one ore multi core each loop cycle, check it once on app startupEmbroidery
@Embroidery static readonly with initializer - it means it is checked once.Rescissory
G
0

To expound on a comment CodeInChaos made:

You can set a given thread's priority. Threads are scheduled for execution based on their priority. The scheduling algorithm used to determine the order of thread execution varies with each operating system. All threads default to "normal" priority, but if you set your loop to low; it shouldn't steal time from threads set to normal.

Gift answered 13/9, 2011 at 13:51 Comment(0)
P
0

The Timer approach is probably your best bet, but since you mention Thread.Sleep there is an interesting Thread.SpinWait or SpinWait struct alternative for similar problems that can sometimes be better than short Thread.Sleep invocations.

Also see this question: What's the purpose of Thread.SpinWait method?

Periodate answered 11/9, 2012 at 9:40 Comment(0)
T
0

Lots of "advanced" answers here but IMO simply using a Thread.Sleep(lowvalue) should suffice for most.

Timers are also a solution, but the code behind a timer is also an infinity loop - I would assume - that fires your code on elapsed intervals, but they have the correct infinity-loop setup.

If you need a large sleep, you can cut it into smaller sleeps.

So something like this is a simple and easy 0% CPU solution for a non-UI app.

static void Main(string[] args)
{
    bool wait = true;
    int sleepLen = 1 * 60 * 1000; // 1 minute
    while (wait)
    {

        //... your code

        var sleepCount = sleepLen / 100;
        for (int i = 0; i < sleepCount; i++)
        {
            Thread.Sleep(100);
        }
    }
}

Regarding how the OS detects if the app is unresponsive. I do not know of any other tests than on UI applications, where there are methods to check if the UI thread processes UI code. Thread sleeps on the UI will easily be discovered. The Windows "Application is unresponsive" uses a simple native method "SendMessageTimeout" to see detect if the app has an unresponse UI.

Any infinity loop on an UI app should always be run in a separate thread.

Tallinn answered 20/5, 2016 at 10:33 Comment(0)
P
0
while (!cancellationTokenSource.Token.IsCancellationRequested) 
{
    date = DateTime.Now;
    //do work
    Thread.Sleep(10);<< reduce CPU 
}
Pase answered 10/6 at 19:29 Comment(1)
Thank you for your interest in contributing to the Stack Overflow community. This question already has quite a few answers—including one that has been extensively validated by the community. Are you certain your approach hasn’t been given previously? If so, it would be useful to explain how your approach is different, under what circumstances your approach might be preferred, and/or why you think the previous answers aren’t sufficient. Can you kindly edit your answer to offer an explanation?Bathos
M
-3

To keep console applications running just add a Console.ReadLine() to the end of your code in Main().

If the user shouldn't be able to terminate the application you can do this with a loop like the following:

while (true){
   Console.ReadLine();
}
Millrace answered 13/9, 2011 at 13:0 Comment(3)
Seems like his application is better off not having a UI. Btw, once the Console.ReadLine() is reached the first time...the application will just wait for a key press until it commences the next iteration. Someone would have to press a key every minute....seems like an episode of Lost.Sonni
@Millrace I still want to have some control over the moment that the app will gracefully close. So a loop seems a good candidateExothermic
How do you know when to exit the application? Maybe you can perform this check in a timer callback. Then you can close the app by calling Environment.Exit(0).Millrace

© 2022 - 2024 — McMap. All rights reserved.