-1

I need to stop a task from running when the user hits the escape key. The problem is the task is running a method like:

void Start(){

    while(long loop){
        while(!userEvent) System.Windows.Forms.Application.DoEvents();
        for(another long loop){
             while(more loops)
        }
    }
}

And all I've learned browsing the forums on this topic is that to use CancellationToken you need to handle it like this:

if (token.IsCancellationRequested)
{
        // Clean up here, then...
        "cleanup".Dump();
        token.ThrowIfCancellationRequested();
}

Since there are so many loops, checking if the cancellation is requested would be highly inefficient and ugly to read.

The task needs to stop when the window the events inside are being read off of so I can't just check the CancellationToken before the next iteration, it would need to be pretty much everywhere

So, is there a way to stop the task whatever loop it's in? It doesn't need to be with CancellationTokens but it seems to be the "standard" way of doing this.

Also, the method Start() wasn't used as a Task at first but that would make it so that even after closing the window the main thread kept running the code unless I used a bunch of if (windowClosed) break which were equally impractical.

mjgalindo
  • 856
  • 11
  • 26
  • 3
    When you use a `throw` to cancel, you only need the `if (token.IsCancellationRequested)` in the inner loop (or 1 or 2 levels above that, depending on acceptable latency). – H H Jul 08 '16 at 10:07
  • @HenkHolterman I guess I forgot to add that the task needs to stop when I close the window the events are being read off of. For this, the thread would get stuck in the `while(!userEvent) ... ` so the latency would be too great. – mjgalindo Jul 08 '16 at 10:11
  • I don't see the problem/difference. The OnClosing event can raise the Cancelrequest, right? – H H Jul 08 '16 at 10:16
  • @HenkHolterman but I still need to check if it's requested inside the task and that's what I'm trying to avoid. I first thought that the task would stop without checking the CancellationToken but it doesn't seem to be that convenient. – mjgalindo Jul 08 '16 at 10:19
  • 1
    If you have long-running synchronous method, then it may be easier to have several end checks. There is nothing bad to have several loops containing such checks. Of course you can split method into multiple parts and iterate those parts, performing end check before/after each iteration. Btw, whatever you are doing - you doing it wrong, because of `DoEvents` (which is a typical quick-fix of wrong design). – Sinatr Jul 08 '16 at 10:30
  • Is Start running on the UI thread? It would be better to run it on a background thread and then you wouldn't need the DoEvents() call. – Martin Brown Jul 08 '16 at 10:34
  • @MartinBrown It's running on the Main Thread. I tried running it in a separate thread but it wouldn't work like that. – mjgalindo Jul 08 '16 at 10:42
  • @Sinatr I think you're right but It won't let me run the method in a separate thread. – mjgalindo Jul 08 '16 at 10:43

2 Answers2

2

Exception just propagates through no matter how many loops you are in.

private void Form1_KeyDown(object sender, KeyEventArgs e)
{
    switch (e.KeyCode)
    {
        case Keys.Escape:
            cancallation.Cancel();
            break;
    }
}

private CancellationTokenSource cancallation = new CancellationTokenSource();

private void button1_Click(object sender, EventArgs e)
{
    Task.Run(() =>
    {
        while (...)
        {
            for (...)
            {
                cancallation.Token.ThrowIfCancellationRequested();
            }
        }
    }, cancallation.Token);
}

private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
    if (cancallation.IsCancellationRequested == false)
    {
        cancallation.Cancel();
    }
}
Tommy
  • 3,044
  • 1
  • 14
  • 13
  • Whilst I find throwing an exception just to get out of a loop a bit meh, I do like this solution. – Neil Jul 08 '16 at 10:54
1

This is a use-case for the goto statement (see https://msdn.microsoft.com/en-us/library/13940fs2.aspx).

    void nestedExit()
    {
        while (true)
        {
            while (true)
            {
                while (true)
                {
                    while (true)
                    {
                        while (true)
                        {
                            goto Done;
                        }
                    }
                }
            }
        }

    Done:
        ;
    }

Note: The semicolon is necessary to make the line a statement. Just the label and the closing brace does not compile.