1

I have code that creates a CancellationTokenSource and that passes it to a method.

I have code in another are of the app that issues a cts.Cancel();

Is there a way that I can cause that method to stop immediately without me having to wait for the two lines inside the while loop to finish?

Note that I would be okay if it caused an exception that I could handle.

public async Task OnAppearing()
{
   cts = new CancellationTokenSource();
   await GetCards(cts.Token);
}

public async Task GetCards(CancellationToken ct)
{
   while (!ct.IsCancellationRequested)
   {
      App.viewablePhrases = App.DB.GetViewablePhrases(Settings.Mode, Settings.Pts);
      await CheckAvailability();
   }
}
Gleb
  • 1,723
  • 1
  • 11
  • 24
Alan2
  • 23,493
  • 79
  • 256
  • 450
  • `ct.ThrowIfCancellationRequested`? – Pavel Anikhouski Dec 09 '19 at 08:08
  • 2
    Cancellation is cooperative. The *older* model it replaced was threads and thread.abort, which does what you ask for but is so difficult to use *correctly* (in the general case) that it's mostly abandoned. – Damien_The_Unbeliever Dec 09 '19 at 08:09
  • @Damien_The_Unbeliever Thread.Abort isn't implemented in .NET Core. But, yeah, it would work in .NET Framework. – AngryHacker Dec 09 '19 at 08:16
  • Related: [How to transform task.Wait(CancellationToken) to an await statement?](https://stackoverflow.com/questions/25632803/how-to-transform-task-waitcancellationtoken-to-an-await-statement) – Theodor Zoulias Dec 16 '20 at 15:57

2 Answers2

5

What I can suggest:

  1. Modify GetViewablePhrases and CheckAvailability so you could pass the CancellationToken to them;
  2. Use ct.ThrowIfCancellationRequested() inside these functions;
  3. Try / Catch the OperationCanceledException inside GetCards;

As for your your functions I don't know how exactly they work inside. But let's assume you have a long running iteration inside one of them:

CheckAvailability(CancellationToken ct)
{
    for(;;) 
    {
        // if cts.Cancel() was executed - this method throws the OperationCanceledException
        // if it wasn't the method does nothing
        ct.ThrowIfCancellationRequested(); 
        ...calculations... 
    } 
}

Or let's say you are going to access your database inside one of the function and you know that this process is going to take a while:

CheckAvailability(CancellationToken ct)
{
    ct.ThrowIfCancellationRequested();
    AccessingDatabase();
}

This will not only prevent your functions from proceeding with execution, this also will set the executioner Task status as TaskStatus.Canceled

And don't forget to catch the Exception:

public async Task GetCards(CancellationToken ct)
{
   try
   {
      App.viewablePhrases = App.DB.GetViewablePhrases(Settings.Mode, Settings.Pts, ct);
      await CheckAvailability(ct);
   }
   catch(OperationCanceledException ex)
   {
       // handle the cancelation...
   }
   catch
   {
       // handle the unexpected exception
   }
}
Gleb
  • 1,723
  • 1
  • 11
  • 24
  • Can you give an example of how I would use ct.ThrowIfCancellationRequested inside a function. Just a very simple example would be a help. – Alan2 Dec 09 '19 at 08:22
  • 1
    @Alan2 I slightly modified my answer for you. I hope it helps. – Gleb Dec 09 '19 at 08:53
1

If you are OK with cancelling not the task, but the awaiting of the task, you could use a cancelable wrapper. In case of cancellation the underlying task will continue running, but the wrapper will complete immediately as canceled.

public static Task AsCancelable(this Task task,
    CancellationToken cancellationToken)
{
    var cancelable = new Task(() => { }, cancellationToken);
    return Task.WhenAny(task, cancelable).Unwrap();
}

public static Task<T> AsCancelable<T>(this Task<T> task,
    CancellationToken cancellationToken)
{
    var cancelable = new Task<T>(() => default, cancellationToken);
    return Task.WhenAny(task, cancelable).Unwrap();
}

Usage example:

await GetCards(cts.Token).AsCancelable(cts.Token);

This extension method can also be implemented using a TaskCompletionSource<T> (instead of the Task<T> constructor).

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104