Search code examples
c#while-looptaskabort

How to get out a while loop when in Task.Delay


I am trying to stop a while loop in my program when an abort key is pressed, and the function running is running a Task.Delay. Unfortunately, even though this must be easy to do I just cannot get it to work for me. Please help.

I have a button that asks the user to confirm they want to run and if yes it comes to the function below and starts to run the RunSequence function. I did have this on a new thread but have now changed it to a Task, I leave the commented out code in just in case I need to run it instead of a task. RunSequence has two parameters the second is what I think I should have and that is a CancellationToken.

CancellationTokenSource tokenSource = new CancellationTokenSource();

    private void ConfirmRunSequence()
    {
    //put it on a thread as the UI is slow to update
    //var thread = new Thread(() => { RunSequence(_filePathName, tokenSource.Token); });
    //thread.IsBackground = true;
    //thread.Start();
    
    Task.Run(() => RunSequence(_filePathName, tokenSource.Token), tokenSource.Token);
    }

When the abort button is pressed, we set the Token to cancel and I want to drop out the While loop.

private void onAbort()
{
Abort = true; //set to abort sequence
            tokenSource.Cancel();
}

I hopefully have the bits above correct, and I think the next bit is what I do not understand. Here I have a CancellationToken called _ct which I believe is tokenSource. My delay here is big so when I see the label update a few times I will then click to abort and it will be inside the delay which I want to cancel. Now this is what I cannot get to work.

I get a red sqiggly under _ct and it says “Cannot convert from System.Threading.CancellationToken to System.Threading.Task.Task”. Ok I read the words but sorry I do not know how to fix it but I also do not know if I did fix it if this is the correct way to get out the While loop, please help.

private async void RunSequence(string filePath, CancellationToken _ct)
{
    Int count = 0;

while (!sr.EndOfStream) 
{
    lbl_count = count++;
    await Task.WhenAny(Task.Delay(10000), _ct);
}

lbl_count =”aborted”;
}

Amongst the things I have tried is to change from await Task.WhenAny(Task.Delay(10000), _ct); to Just Task.Delay(10000, _ct) but also no good.


Solution

  • I made a small app to show how I got my app to work. Create a small C# Winform .Net with two buttons one to run and one to abort and a label. As there was a request for the code I have included the full example program at Github

    https://github.com/zizwiz/Cancellation_Token_Example

    I also add a copy of some of the code below:

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    
    namespace Cancel_Token_Example
    {
        public partial class Form1 : Form
        {
    
            CancellationTokenSource tokenSource; // Declare the cancellation token
    
            public Form1()
            {
                InitializeComponent();
            }
    
            private void btn_run_Click(object sender, EventArgs e)
            {
                tokenSource = new CancellationTokenSource();    //Make a new instance
                Task.Run(() => RunSequence(tokenSource.Token)); //Run the task that we need to stop
            }
    
            private void btn_abort_Click(object sender, EventArgs e)
            {
                tokenSource.Cancel(); // make the token a cancel token
            }
    
            private async void RunSequence(CancellationToken _ct)
            {
                int counter = 0;
    
                while (!_ct.IsCancellationRequested)
                {
                    // show incrementing number but as we have a task watch for cross threading
                    WriteUIData((counter++).ToString());
    
                    try
                    {
                        await Task.Delay(1000, _ct); //waits 1 second
                    }
                    catch
                    {
                        // Do nothing just needed so we can exit without exceptions
                    }
    
                }
    
                if (_ct.IsCancellationRequested)
                {
                    //report we have cancelled
                    WriteUIData("Cancelled");
                }
    
                tokenSource.Dispose(); //dispose of the token so we can reuse
            }
    
            private void WriteUIData(String data)
            {
                // Write data to UI but as we have a task watch for cross threading
    
                if (lbl_output.InvokeRequired)
                {
                    lbl_output.BeginInvoke((MethodInvoker)delegate () { lbl_output.Text = data; });
                }
                else
                {
                    lbl_output.Text = data;
                }
            }
        }
    }