CPU friendly infinite loop

前端 未结 11 1902
心在旅途
心在旅途 2020-12-04 09:15

Writing an infinite loop is simple:

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

But this will trash the CPU performance. This ex

相关标签:
11条回答
  • 2020-12-04 09:33

    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");
        }
    }
    
    0 讨论(0)
  • 2020-12-04 09:39

    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.");
    }
    
    0 讨论(0)
  • 2020-12-04 09:41

    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)

    0 讨论(0)
  • 2020-12-04 09:42

    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.

    0 讨论(0)
  • 2020-12-04 09:42

    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();
    }
    
    0 讨论(0)
  • 2020-12-04 09:43

    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.

    0 讨论(0)
提交回复
热议问题