1

I have Two button for scenarios where one initiate the task and other stop that task.

   // this is the property which is used to cancel the task
   CancellationTokenSource cTokenSource;

private async void OnReadCommand()
{
   cTokenSource = new CancellationTokenSource();
   ReadAction();
}


private async void ReadAction()
{
   Task.Factory.StartNew(() => {
       while (true)
       {
           Task.Delay(TimeSpan.FromSeconds(2)).Wait();
        //writing in debug console
           Debug.WriteLine($"Some Random Nos : {Guid.NewGuid().ToString()}");
        //sending it to the ui for testing
           uiContext.Send(x => MessageArea2Content.Message = Guid.NewGuid().ToString(), null);
       }
   },cTokenSource.Token);
}
       
private async void OnCancelCommand()
{
   // it's used to cancel the task
   cTokenSource.Cancel();
}

my app is wpf and using mvvm pattern and prism library. while calling the OnCancelCommand the task is running in background and printing the GUID. I have to cancel the task only and only on OnCancelCommand.

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
CodeJunkie
  • 13
  • 5
  • While this is a WinForms sample, https://github.com/lextm/backgroundworker-sample/blob/master/BackgroundWorker/Form1.cs you can find clearly how `CancellationTokenSource` is supposed to be used. Your loop didn't check for cancellation so what you observed is by design. – Lex Li Aug 22 '22 at 05:54
  • Thanks @LexLi for reply. I got the point, I use loop to check for progressing in real scenario i have more steps in between if cancel is called i need to stop it. – CodeJunkie Aug 22 '22 at 06:29
  • Note that instead of looping around a Task.Delay you should really be using a [timer](https://learn.microsoft.com/en-us/dotnet/api/system.timers.timer?view=net-6.0). This also makes it trivial to cancel, just stop the timer. `Task.Delay` is already a wrapper around a timer, so you are really just adding a bunch of complexity without much gain. – JonasH Aug 22 '22 at 09:49

1 Answers1

1

You need to implement the code for exiting the loop yourself:

private async void ReadAction()
{
    Task.Factory.StartNew(() => {
        while (true)
        {
            Task.Delay(TimeSpan.FromSeconds(2)).Wait();
            //writing in debug console
            Debug.WriteLine($"Some Random Nos : {Guid.NewGuid().ToString()}");
            //sending it to the ui for testing
            uiContext.Send(x => MessageArea2Content.Message = Guid.NewGuid().ToString(), null);
            
            cTokenSource.Token.ThrowIfCancellationRequested();
        }
    },cTokenSource.Token);
}

or

private async void ReadAction()
{
    Task.Factory.StartNew(() => {
        while (true)
        {
            Task.Delay(TimeSpan.FromSeconds(2)).Wait();
            //writing in debug console
            Debug.WriteLine($"Some Random Nos : {Guid.NewGuid().ToString()}");
            //sending it to the ui for testing
            uiContext.Send(x => MessageArea2Content.Message = Guid.NewGuid().ToString(), null);
            
            if (cTokenSource.Token.IsCancellationRequested)
            {
                break; // or return;
            }
        }
    },cTokenSource.Token);

Usage examples you can find in the documenation Task Class. The only benefit that you get from passing CancellationToken to StartNew method is that it can automatically cancel the task for you if the source is cancelled before the task started. However to cancel it during the run you need to code the logic yourself. Here is another good reading on that https://stackoverflow.com/questions/48312544/whats-the-benefit-of-passing-a-cancellationtoken-as-a-parameter-to-task-run#:~:text=In%20summary%2C%20providing%20the%20cancellation,to%20start%20running%20the%20task.

Furthermore, I would suggest to you using await in the logic that you execute in StartNew so that you do not loose the benefits of being asynchronous:

private async void ReadAction()
{
    Task.Factory.StartNew(async () => {
        while (true)
        {
            await Task.Delay(TimeSpan.FromSeconds(2));
            //writing in debug console
            Debug.WriteLine($"Some Random Nos : {Guid.NewGuid().ToString()}");
            //sending it to the ui for testing
            uiContext.Send(x => MessageArea2Content.Message = Guid.NewGuid().ToString(), null);
            
            if (cTokenSource.Token.IsCancellationRequested)
            {
                break; // or return;
            }
        }
    },cTokenSource.Token)

That way threads will no longer get blocked and will be released during a call to Delay.

mr100
  • 4,340
  • 2
  • 26
  • 38
  • Thanks @mr100 reply. This loop is for testing purpose Only but my real function has many steps to proceed in between if user called the cancel button i need to stop. – CodeJunkie Aug 22 '22 at 06:30
  • Then such a logic you could potentially execute in if (cTokenSource.Token.IsCancellationRequested) section or somewhere else. Bottom line is, if you want to cancel the task you need to code the logic to stop it yourself (or pass the token further to some async method you are calling that has the logic to stop itself). – mr100 Aug 22 '22 at 06:49