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?
A task is only considered "cancelled" if
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With