Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cancelling a task within a task

I am trying to cancel a Task<> by calling the CancellationTokenSource.Cancel() method within the task, but I cannot get it to work.

Here is the code I am using:

TaskScheduler ts = TaskScheduler.Current;

CancellationTokenSource cts = new CancellationTokenSource();

Task t = new Task( () =>
{
    Console.WriteLine( "In Task" );
    cts.Cancel();
}, cts.Token );

Task c1 = t.ContinueWith( antecedent =>
{
    Console.WriteLine( "In ContinueWith 1" );
}, CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, ts );

Task c2 = c1.ContinueWith( antecedent =>
{
    Console.WriteLine( "In ContinueWith 2" );
}, TaskContinuationOptions.NotOnCanceled );

t.Start();

Console.ReadKey();

Environment.Exit( 1 );

This print outs:

In Task
In ContinueWith 1
In ContinueWith 2

What I expected was this:

In Task

Am I missing something here? Can tasks only be cancelled outside of the task?

like image 903
Intrepid Avatar asked Nov 12 '12 13:11

Intrepid


1 Answers

A task is only considered "cancelled" if

  • Its cancellation token is cancelled before it starts executing.
  • Its cancellation token is cancelled while it is executing and the code cooperatively observes the cancellation by throwing the OperationCancelledException (usually by the code calling cts.Token.ThrowIfCancellationRequested())

If you added a cts.Token.ThrowIfCancellationRequested() line after the cts.Cancel() then things would behave as you expect. In your example, the cancellation takes place while the task is running, but the task doesn't observe the cancellation, and the task's action runs to completion. So the task is marked as "Ran to Completion".

You can check for the case of a task that "Ran to Completion" but was cancelled (either during or after the completion of the task) by checking the cancellation token within the continuation (cts.Token.IsCancellationRequested). Another option that sometimes helps is to use the original cancellation token as the cancellation token for the continuation (that way, if the the cancellation is not noticed by the antecedent task, it will at least be respected by the continuation)--since the TPL will mark the continuation as cancelled before it even has a chance to run.

like image 82
Matt Smith Avatar answered Oct 31 '22 02:10

Matt Smith