6

In short, I'm utilizing C# to scientific computation and I've written a method that has a while loop that may run to a user-specified quantity of steps... Actually, this method may take too long to execute (like more than 5 hours). When it takes this long, I may want to stop the method pressing Esc key, for example.

As I read something about breaking while, it is as simple as a Boolean flag or something like this. So I thought in something like this:

public Double? Run(int n)
{
    int i = 0;
    while ((i < n) && (/* inputkey != ConsoleKey.Escape */))
    {
        // here goes the heavy computation thing
        // and I need to read some "inputkey" as well to break this loop
        i++;
    }
    // I'm not worried about the return statement, as it is easy to do...
    // returns null if the user skipped the method by pressing Escape
    // returns null if the method didn't converged
    // returns the double value that the method calculated otherwise
}

Well, this is what I wondered until now... So please, could you give useful ideas to this extent? How can I wait for a user input (I thought about Events, but I'm not sure how to implement it here and I think that it will make the code even slower, if I have to listen to a key at every while step the code goes into...

Well, any ideas or comments?


Update: I think I should have had described better the problem. All the solutions you gave me may solve this problem I proposed, but I think I was not completely reliable to my real problem. I don't know if I should ask another question or keep with this one...

amiregelz
  • 1,833
  • 7
  • 25
  • 46
Girardi
  • 2,734
  • 3
  • 35
  • 50

5 Answers5

6

You could run this method from a separate thread and set a stop variable when a key is pressed:

object myLock = new object();
bool stopProcessing = false;

public Double? Run(int n)
{
    int i = 0;
    while (i < n)
    {
        lock(myLock)
        {
            if(stopProcessing)
                break;
        }
        // here goes the heavy computation thing
        // and I need to read some "inputkey" as well to break this loop
        i++;
    }
}

and when a key is pressed, update stopProcessing accordingly:

Console.ReadKey();
lock(myLock)
{
    stopProcessing = true;
}
Mark Avenius
  • 13,679
  • 6
  • 42
  • 50
5

If you're just wanting to stop the application, Ctrl-C from the command line will do it. If you really need to intercept input during a long running process, you might want to spawn a worker thread to do the long running process and then just use the main thread to interact with the console (i.e. Console.ReadLine()).

Brady Holt
  • 2,844
  • 1
  • 28
  • 34
  • I agree, take a look at the BackgroundWorker: http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx – Tom Vervoort Feb 21 '11 at 16:24
  • 1
    Agreed. Background Worker is the best way to do this. You may even be able to spawn multiple threads so your "long running process" can be a bit less long running. – Nate Noonen Feb 21 '11 at 16:30
  • Well, I've already used BackgroundWorker for a GUI app I builded, but I didn't know I could use it in Console apps as well. Now, regarding your first point, I can't use only CTRL C, as this was just a simple example of what I have... Actually I run the `Run()` method dozens of times and perform an average with the returned values, and I do this for various sets of parameters, intending to write a file in the end of the program... My point is, if I press CTRL C, the programs stops definitely, not only skips a value... You see? Thanks for the tip of the bg worker! – Girardi Feb 21 '11 at 16:37
2

You will need to do this using threading. When you start the task, spawn a new thread and execute the task on that thread. Then in your Program.cs, wait for user input. If the user enters something meaningful - in your case, the Esc key - alert the background thread of the action. The simplest way to do this is by setting a static variable. The background thread will be checking this static variable and when it has been changed, the background thread will clean itself up and abort.

See the MSDN article on Threading.

A code sample will be a little more in depth, but it would look something like this:

public class Program.cs
{
    public static myFlag = false;
    public void Main()
    {
        thread = new Thread(new ThreadStart(DoWork));
        thread.Start();
        Console.ReadLine();
        myFlag = true;
    }
    public static DoWork()
    {
        while(myFlag == false)
        {
            DoMoreWork();
        }
        CleanUp()
    }
    public static DoMoreWork() { }
    public static CleanUp() { }

}
Jeff
  • 13,943
  • 11
  • 55
  • 103
  • I think your idea is more or less Mark's Idea isn't it? Thanks for both! – Girardi Feb 21 '11 at 16:30
  • @Jeff: be careful though; this is not thread-safe without locking (you will encounter a race condition). – Mark Avenius Feb 21 '11 at 16:32
  • @Girardi yes, we suggested pretty much the same thing. – Jeff Feb 21 '11 at 16:39
  • @Mark you're right. i was just trying to provide a sample for Girardi to go with and I wasnt planing on mocking up the whole 9 yards. also, situation sounds pretty simple with a limited audience so the race condition is probably an edge case. still, locking is definitely the right way to go. – Jeff Feb 21 '11 at 16:42
  • @Jeff: I just wanted to point it out; I have actually had a post of mine deleted before because I was not fully thread-safe with an example, so I have learned to be explicit :-) – Mark Avenius Feb 21 '11 at 16:43
  • @Mark wow! there's gotta be a middle ground between providing help and fully developing an idea for someone. thanks for the tip – Jeff Feb 21 '11 at 16:53
2

pool on Console.KeyAvailable in timely manner and take the action accordingly.

Mubashir Khan
  • 1,464
  • 1
  • 12
  • 17
2
using System;
using System.Threading.Tasks;

namespace ConsoleApplication4
{
    class Program
    {
        static bool _cancelled = false;
        static void Main( string[] args )
        {
            var computationTask = Task.Factory.StartNew(PerformIncredibleComputation);
            var acceptCancelKey = Task.Factory.StartNew(AcceptCancel);


        while (!acceptCancelKey.IsCompleted && ! computationTask.IsCompleted)
        {

            computationTask.Wait (100);        
        }

        if( acceptCancelKey.IsCompleted && !computationTask.IsCompleted )
        {
            computationTask.Wait (new System.Threading.CancellationToken ());
        }
        else if(!acceptCancelKey.IsCompleted)
        {
            acceptCancelKey.Wait(new System.Threading.CancellationToken());
        }


    }


    private static void PerformIncredibleComputation()
    {
        Console.WriteLine("Performing computation.");
        int ticks = Environment.TickCount;
        int diff = Environment.TickCount - ticks;
        while (!_cancelled && diff < 10000)
        {
           //computing  
        }
        Console.WriteLine("Computation finished");
    }

    private static void AcceptCancel()
    {

        var key = Console.ReadKey(true);
        Console.WriteLine("Press Esc to cancel");
        while(key.Key != ConsoleKey.Escape)
        {
            key = Console.ReadKey(true);
        }
        _cancelled = true;
        Console.Write("Computation was cancelled");

    }
}

}

Jeff
  • 13,943
  • 11
  • 55
  • 103
Simon Smeets
  • 581
  • 6
  • 17