Re: break out of continuous for loop in key down event?

Tech-Archive recommends: Repair Windows Errors & Optimize Windows Performance



Rich P wrote:
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.

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
.



Relevant Pages

  • Re: Fading effect
    ... Please link to your web page that has the applet. ... private void printThreadName{ ... public void init() { ...
    (comp.lang.java.help)
  • Re: proper programming? (events)
    ... I prefer to place that logic in the event handler, or if the logic is complex ... private readonly UserUICapabilities caps; ... public void BeginCellEdit ... That class may be the only argument to the BeginCellEdit method: ...
    (microsoft.public.dotnet.languages.csharp)
  • Re: Animating auto hide of JPanel inside a JFrame
    ... When the mouse is brought close to the top of the panel, a label 'slides' down ... public void actionPerformed ... private void positionLabel ... Timer class that is called back at intervals. ...
    (comp.lang.java.gui)
  • Re: custom event
    ... But this timer tick event occurs in the class file ... private void button1_Click ... public PeriodicUpload(FTPConfig _config, String _fileToUpload, ... public void changeCongifuration ...
    (microsoft.public.dotnet.languages.csharp)
  • Re: Multi-Threading problem
    ... private int seconds=0; ... private void secondsThread() { ... final MyClock myClock=new MyClock; ... public void run ...
    (comp.lang.java.programmer)