break out of continuous for loop in key down event?

R

Rich P

I have a test sample console app (below - full code - operational) for
experimenting with Delegates and Events. This sample prints out the
current time to the console window every second from a continuous for
loop. I want to break out of this continuous for loop in a key down
event. It was suggested (from a previous post on the same topic) that I
could try Console.ReadKey() (thank you to those who replied to that post
and offered this suggestion) and I could specify a key to break out on.
I did try this - in the for loop, and the problem is that the program
waits for me to press a key for every iteration of the for loop. I
would like the program (the for loop) to run continuously
(uninterrupted) until I press any key to break out of the loop. Would
this involve an additional delegate or even trapping? How could I
implement this functionality into the sample program below?


---------------------------------------------
using System;
using System.Threading;

namespace DelegatesEvents
{
// Our subject -- it is this class that other classes
// will observe. This class publishes one event:
// SecondChange. The observers subscribe to that event.
public class Clock
{
// Private Fields holding the hour, minute and second
private int _hour;
private int _minute;
private int _second;

public delegate void SecondChangeHandler(
object clock,
TimeInfoEventArgs timeInformation
);

// The event we publish
public event SecondChangeHandler SecondChange;

// The method which fires the Event
protected void OnSecondChange(
object clock,
TimeInfoEventArgs timeInformation
)
{
// Check if there are any Subscribers
if (SecondChange != null)
{
// Call the Event
SecondChange(clock, timeInformation);
}
}

// Set the clock running, it will raise an
// event for each new second
public void Run()
{
for (; ; )
{
// Sleep 1 Second
Thread.Sleep(1000);

// Get the current time
System.DateTime dt = System.DateTime.Now;

// If the second has changed
// notify the subscribers
if (dt.Second != _second)
{
// Create the TimeInfoEventArgs object
// to pass to the subscribers
TimeInfoEventArgs timeInformation =
new TimeInfoEventArgs(
dt.Hour, dt.Minute, dt.Second);

// If anyone has subscribed, notify them
OnSecondChange(this, timeInformation);
}

// update the state
_second = dt.Second;
_minute = dt.Minute;
_hour = dt.Hour;

}
}
}

public class TimeInfoEventArgs : EventArgs
{
public TimeInfoEventArgs(int hour, int minute, int second)
{
this.hour = hour;
this.minute = minute;
this.second = second;
}
public readonly int hour;
public readonly int minute;
public readonly int second;
}

/*====Event Subscribers ============== */

// An observer. DisplayClock subscribes to the
// clock's events. The job of DisplayClock is
// to display the current time
public class DisplayClock
{
// Given a clock, subscribe to
// its SecondChangeHandler event
public void Subscribe(Clock theClock)
{
theClock.SecondChange += new
Clock.SecondChangeHandler(TimeHasChanged);
}

public void TimeHasChanged(object theClock, TimeInfoEventArgs ti)
{
Console.WriteLine("Current Time: {0}:{1}:{2}",
ti.hour.ToString(),
ti.minute.ToString(),
ti.second.ToString());
}
}

// A second subscriber whose job is to write to a file
public class LogClock
{
public void Subscribe(Clock theClock)
{
theClock.SecondChange +=
new Clock.SecondChangeHandler(WriteLogEntry);
}

public void WriteLogEntry(
object theClock, TimeInfoEventArgs ti)
{
Console.WriteLine("Logging to file: {0}:{1}:{2}",
ti.hour.ToString(),
ti.minute.ToString(),
ti.second.ToString());
}
}

class Program
{
static void Main(string[] args)
{
// Create a new clock
Clock theClock = new Clock();
// Create the display and tell it to
// subscribe to the clock just created
DisplayClock dc = new DisplayClock();
dc.Subscribe(theClock);
// Create a Log object and tell it
// to subscribe to the clock
LogClock lc = new LogClock();
lc.Subscribe(theClock);
// Get the clock started
theClock.Run();
}
}
}
 
J

Joe Cool

I have a test sample console app (below - full code - operational) for
experimenting with Delegates and Events.  This sample prints out the
current time to the console window every second from a continuous for
loop.  I want to break out of this continuous for loop in a key down
event.  It was suggested (from a previous post on the same topic) that I
could try Console.ReadKey() (thank you to those who replied to that post
and offered this suggestion) and I could specify a key to break out on.
I did try this - in the for loop, and the problem is that the program
waits for me to press a key for every iteration of the for loop.  I
would like the program (the for loop) to run continuously
(uninterrupted) until I press any key to break out of the loop.  Would
this involve an additional delegate or even trapping?  How could I
implement this functionality into the sample program below?

Did you try the Console.Read() method? It returns -1 is there are
currently no more characters to be read.
 
P

Peter Duniho

Rich said:
[...] It was suggested (from a previous post on the same topic) that I
could try Console.ReadKey() (thank you to those who replied to that post
and offered this suggestion) and I could specify a key to break out on.
I did try this - in the for loop, and the problem is that the program
waits for me to press a key for every iteration of the for loop. I
would like the program (the for loop) to run continuously
(uninterrupted) until I press any key to break out of the loop. Would
this involve an additional delegate or even trapping? How could I
implement this functionality into the sample program below?

My recollection is that the same suggestion included a mention of using
the Console.KeyAvailable property to check to see if key input was
available before actually calling ReadKey(). That's one possible solution.

Another possible solution is to separate your processing from your UI.
Have one thread to do the actual processing work, and another that calls
ReadKey() in a loop, checking each key returned and setting a flag that
the processing loop examines if the appropriate key is entered.

For your purposes, the KeyAvailable property is probably the simplest
way to fix the issue. But since you're just experimenting anyway, you
might find it valuable to learn the threading technique too. :)

Pete
 
R

Rich P

Thanks all for your replies:
Did you try the Console.Read() method? It returns -1 is >there are
currently no more characters to be read.

Console.Read() would take any key yes, but it interrupts the loop.
For your purposes, the KeyAvailable property is probably >the simplest
way to fix the issue. But since you're just >experimenting anyway, you
might find it valuable to learn >the threading technique too. :)

Would it be ok to ask for a sample how I would implement the threading
technique? This does sound kind of interesting. A sample how to
implement would be greatly appreciated.

Rich
 
P

Peter Duniho

Rich said:
Thanks all for your replies:

currently no more characters to be read.

Console.Read() would take any key yes, but it interrupts the loop.

way to fix the issue. But since you're just >experimenting anyway, you
might find it valuable to learn >the threading technique too. :)

Would it be ok to ask for a sample how I would implement the threading
technique? This does sound kind of interesting. A sample how to
implement would be greatly appreciated.

Sure. The basic idea is something like this:

class Test
{
volatile bool _fStop;

public void Run()
{
new Thread(_Thread).Start();
while (Console.ReadKey().Key != ConsoleKey.Q) { }
_fStop = true;
}

private void _Thread()
{
while (!_fStop)
{
// do stuff
}
}
}

In the above example, you'd call the Test.Run() method to start things
going. The Run() method creates a new thread for doing the work you
want, which monitors a flag that the original thread that called Run()
will set when the appropriate key is pressed.

If you like, you can easily reverse the threading, so that the new
thread that's started is the one that reads the key, while the original
thread does the processing:

class Test
{
private volatile bool _fStop;

public void Run()
{
new Thread(_Thread).Start();
while (!_fStop)
{
// do stuff
}
}

private void _Thread()
{
while (Console.ReadKey().Key != ConsoleKey.Q) { }
_fStop = true;
}
}

One advantage of that second approach is that it doesn't introduce any
new external threading behavior. That is, a new thread is created, but
the actual work being done is still done on the original thread, so
client code (such as that subscribing to an event in the class) doesn't
have to worry about being called on a thread different from the one it
was originally being executed in.

If you like, you can even encapsulate the console key checking behavior
in a separate class, allowing for general purpose reuse of the approach:

class Test
{
private volatile bool _fStop;

public void Run()
{
ConsoleKeyChecker checker = new
ConsoleKeyChecker(ConsoleKey.Q);

checker.Quit += (sender, e) => _fStop = true;
checker.Start();

while (!_fStop)
{
// do stuff
}
}
}

class ConsoleKeyChecker
{
private ConsoleKey _keyQuit;

public ConsoleKeyChecker(ConsoleKey keyQuit)
{
_keyQuit = keyQuit;
}

public void Start()
{
new Thread(_Thread).Start();
}

public event EventHandler Quit;

private void _OnQuit()
{
EventHandler handler = Quit;

if (handler != null)
{
handler(this, EventArgs.Empty);
}
}

private void _Thread()
{
while (Console.ReadKey().Key != _key) { }
_OnQuit();
}
}

A couple of notes on the above:

-- In my example, I've used a lambda expression to declare an
anonymous method used as the event handler for the
ConsoleKeyChecker.Quit event. IMHO, this provides a nice concise way to
declare simple logic like setting a boolean, but of course you could
declare a named method and use that as the event handler in the
traditional way.

-- Note my implementation of the _OnQuit() method. It saves the
value of the event delegate field to a local variable before checking
for "null". This prevents a race condition from causing you to get a
null reference exception when trying to raise the event. In your own
code you posted earlier, if the thread raising the event is different
from another thread modifying the event (specifically, by removing the
last delegate from the event), you could find the event field non-null
during the "if" statement conditional, but then null at the next
statement when you try to raise the event.

If you don't intend to have multi-threaded support, then this feature
isn't necessary. But in my example, I'm specifically creating a new
thread, and while in most of those variations that doesn't expose a
threading problem to the event subscription, I like to see thread safety
when threads are introduced. Of course, in my very first example,
because it exposes the threading to the client code, it actually does
introduce a potential thread-safety issue, making handling this case
that much more important.

One final note on the thread-safe event-raising issue: an alternative
approach to using a local variable is to simply pre-initialize the event
to have an empty event handler:

public event EventHandler Quit = delegate { };

Then you don't even need to check for null. There will always be that
one delegate reference to the empty anonymous method and a
null-reference exception can never happen.

I prefer the "local-variable, check for null" approach, but the other
"pre-initialize" pattern is fine as long as you don't mind an extra
object being allocated for every event you declare (and frankly, in most
cases that simply isn't a concern).

Pete
 
J

Jeff Johnson

Sure. The basic idea is something like this:

class Test
{
volatile bool _fStop;

public void Run()
{
new Thread(_Thread).Start();
while (Console.ReadKey().Key != ConsoleKey.Q) { }
_fStop = true;
}

private void _Thread()
{
while (!_fStop)
{
// do stuff
}
}
}

Is it required to mark the flag volatile here? Only one place in the code
seems to be accessing it. I was just wondering if that was simply your
general habit or if it's actually a necessity.
 
P

Peter Duniho

Jeff said:
Is it required to mark the flag volatile here? Only one place in the code
seems to be accessing it. I was just wondering if that was simply your
general habit or if it's actually a necessity.

It's definitely necessary. The variable is used in two different
places. It's assigned in the Run() method, and read in the _Thread()
method, each of which are executing in different threads.

On x86 the main issue is inhibiting compiler optimizations that would
hoist the variable read out of the loop, but on other platforms there
could be hardware-level ordering issues too.

Pete
 
R

Rich P

This/these are great examples. But I got caught up in Real work :) all
of a sudden (always in the middle of my fun :(

A store procedure (of my creation) went south all of a sudden (after
years of working correctly -- we did a server migration a few weeks ago
- I thought I covered all the sp's).

You remember how to work a sql server 2000 enterprise debugger? Neither
do I. Some crazy way of delimiting parameters. I have to wait for
someone to get back to me on how to delimit these buggers for the
debugger. I will try your sample code out tommorrow first thing. Can't
wait to try it.

Rich
 
R

Rich P

Here is my implementation of the suggested example (the example using
the Lambda). The program runs - but it doesn't quit when I press q (Q):

using System;
using System.Threading;

namespace DelegatesEvents
{
public class Clock
{
private int _hour;
private int _minute;
private int _second;
private volatile bool _fStop;

public delegate void SecondChangeHandler(object clock,
TimeInfoEventArgs timeInformation);

public event SecondChangeHandler SecondChange;

protected void OnSecondChange(object clock, TimeInfoEventArgs
timeInformation)
{
if (SecondChange != null)
{
SecondChange(clock, timeInformation);
}
}

public void Run()
{
ConsoleKeyChecker checker = new ConsoleKeyChecker(ConsoleKey.Q);

checker.Quit += (sender, e) => _fStop = true;
checker.Start();

while (!_fStop)
{
for (; ; )
{
Thread.Sleep(1000);

System.DateTime dt = System.DateTime.Now;

if (dt.Second != _second)
{
TimeInfoEventArgs timeInformation =
new TimeInfoEventArgs(
dt.Hour, dt.Minute, dt.Second);

OnSecondChange(this, timeInformation);
}

_second = dt.Second;
_minute = dt.Minute;
_hour = dt.Hour;
}
}

}
}

public class TimeInfoEventArgs : EventArgs
{
public TimeInfoEventArgs(int hour, int minute, int second)
{
this.hour = hour;
this.minute = minute;
this.second = second;
}
public readonly int hour;
public readonly int minute;
public readonly int second;
}

public class DisplayClock
{
public void Subscribe(Clock theClock)
{
theClock.SecondChange += new
Clock.SecondChangeHandler(TimeHasChanged);
}

public void TimeHasChanged(object theClock, TimeInfoEventArgs ti)
{
Console.WriteLine("Current Time: {0}:{1}:{2}",
ti.hour.ToString(),
ti.minute.ToString(),
ti.second.ToString());
}
}

public class LogClock
{
public void Subscribe(Clock theClock)
{
theClock.SecondChange += new
Clock.SecondChangeHandler(WriteLogEntry);
}

public void WriteLogEntry(object theClock, TimeInfoEventArgs ti)
{
Console.WriteLine("Logging to file: {0}:{1}:{2}",
ti.hour.ToString(),
ti.minute.ToString(),
ti.second.ToString());
}
}

class ConsoleKeyChecker
{
private ConsoleKey _keyQuit;

public ConsoleKeyChecker(ConsoleKey keyQuit)
{
_keyQuit = keyQuit;
}

public void Start()
{
new Thread(_Thread).Start();
}

public event EventHandler Quit;

private void _OnQuit()
{
EventHandler handler = Quit;

if (handler != null)
{
handler(this, EventArgs.Empty);
}
}

private void _Thread()
{
while (Console.ReadKey().Key != _keyQuit) { }
_OnQuit();
}
}

class Program
{
static void Main(string[] args)
{
Clock theClock = new Clock();
DisplayClock dc = new DisplayClock();
dc.Subscribe(theClock);

LogClock lc = new LogClock();
lc.Subscribe(theClock);

theClock.Run();
}
}
}


Rich
 
P

Peter Duniho

Rich said:
Here is my implementation of the suggested example (the example using
the Lambda). The program runs - but it doesn't quit when I press q (Q):

No, that's true, it wouldn't. You've simply wrapped an infinite "for"
loop in the "while" loop that checks the boolean "_fStop". But the
infinite "for" loop never exits, so of course the boolean is never
inspected. So even though it does get set to "true", that has no effect
on the flow of your program.

Infinite loops do have this nasty habit of making the program operate in
an infinite way. :) If you'd prefer it not do that, I recommend taking
out the infinite loop. Just stick with the non-infinite "while" loop. :)

Pete
 
R

Rich P

Infinite loops do have this nasty habit of making the >program operate
in an infinite way. :) If you'd prefer it >not do that, I recommend
taking out the infinite loop. >Just stick with the non-infinite "while"
loop. :)

Ah yes. Much better! In your example I interpreted

While(...)
{
do stuff

}

to include my infinite for loop. This while loop is much better - way
more control. I removed the for (...) { part and just left the innards
of the for loop in the while loop. And thus I pursued this exercise to
learn this.

Thank you very much, and thanks to all for replies to my post.

Rich
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top