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.
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;
}
}
}
}