user3294629
user3294629

Reputation: 95

Creating infinite loop without breaking program

I have a game grid that updates in "steps"(Conway's game of life, though that isn't related to my problem in particular). I'm trying to create a Play button that runs the simulation automatically until the button is pressed again to pause the simulation. At first I had something like this:

public partial class MainWindow : Window
{
    bool running = true;
    public MainWindow()
    {   
        InitializeComponent();
        bool isProgramRunning = true;
        while(isProgramRunning)
        {
            while(running)
            ProcessGeneration();
        }
    }
}

And my button that plays/pauses the simulation has the following click handler:

private void PlayClick_Handler(object sender, RoutedEventArgs e)
{           
     PlayButton.Content = ((string)PlayButton.Content == "Play") ? "Pause" : "Play";

     running = (running) ? false : true;
}

I thought that this would let my program simply keep looping (isProgramRunning never ends) repeatedly checking whether "running" is true or false, and that pressing the button to toggle "running" would allow me to loop/break out of the loop. But just the while(isProgramRunning) part kills the program (it won't even load). Actually every time I try to use a while(), the program stops responding. Is there a way to get around this?

Upvotes: 3

Views: 5807

Answers (3)

Scott Chamberlain
Scott Chamberlain

Reputation: 127563

Likely you don't want your ProcessGeneration() to happen as fast as possible, everything on the screen would be a blur. Also you don't want to block on the UI thread. It is possible to kill two birds with one stone, a Timer.

Create a timer and have it run every 1/4 second or however often you want it to update. Then in your start and stop code you just need to enable or disable the timer.

public partial class MainWindow : Window
{
    private readonly System.Timers.Timer _timer;

    public MainWindow()
    {   
        InitializeComponent();

        _timer = new Timer(250); //Updates every quarter second.
        _timer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
    }

    private void OnTimedEvent(object source, ElapsedEventArgs e)
    {
        ProcessGeneration();
    }

    private void PlayClick_Handler(object sender, RoutedEventArgs e)
    {           
         var enabled = _timer.Enabled;
         if(enabled)
         {
             PlayButton.Content = "Play";
             _timer.Enabled = false;
         }
         else
         {
             PlayButton.Content = "Pause";
             _timer.Enabled = true;
         }
    }

}

Upvotes: 8

Eric Lippert
Eric Lippert

Reputation: 660048

Scott Chamberlain's suggestion is correct. I would add to it that it helps to understand why your program stops responding. It's because there's another infinite loop in there that you don't see. It looks like this:

while(true)
{
    get the next notification from the operating system
    if its a quit message, exit
    otherwise, run the event handler associated with the message
}

This loop gets a message that says "the program is starting", so it runs your constructor, which then sits there in a loop forever. It never returns back to the message loop, so the mouse click message is never fetched from the queue, never processed, and therefore your loop doesn't stop.

A timer is a good idea. There are other techniques you can use. I don't recommend creating a worker thread; multithreading introduces a huge amount of complexity into your program. I recommend against using DoEvents also; it starts up a second message loop recursively, which can introduce re-entrancy bugs. I do recommend using await in C# 5, though it can be a bit tricky to wrap your head around.

Upvotes: 8

Andrius Naruševičius
Andrius Naruševičius

Reputation: 8578

To not stop the program's main thread from running, add a separate thread for ProcessGeneration method. On button click, stop that thread. Also, it really depends what processes you are running. If they are not continuous, do as @ScottChamberlain said and use the timer. Howerver if you, need a certain action being executed all the time, use a separate thread for it so it wouldn't block your main thread.

Upvotes: 0

Related Questions