Continuation tasks in .NET TPL: cancelling continuation tasks
March 21, 2014 Leave a comment
Tasks in .NET TPL make it easy to assign tasks that should run upon the completion of a certain task.
We saw in previous posts on TPL how to define continuation tasks. You’ll also find examples showing how to cancel tasks in TPL. Continuation tasks can be cancelled using the same techniques as with “normal” tasks. If you don’t know the basics, then make sure to check out the related posts on this page.
We declare the cancellation token source:
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
We then continue with the antecedent task and provide the token source in the constructor:
Task motherTask = Task.Factory.StartNew(() => { Console.WriteLine("Mother task running."); cancellationTokenSource.Token.WaitHandle.WaitOne(); cancellationTokenSource.Token.ThrowIfCancellationRequested(); }, cancellationTokenSource.Token);
We wait indefinitely for the token source to be cancelled with the WaitOne() method.
The following continuation task is created with the same cancellation token. This has the effect that if the token source is cancelled then both the antecedent and the continuation tasks will be cancelled:
Task taskSharingCancellationToken = motherTask.ContinueWith(antecedent => { Console.WriteLine("This continuation task will never run as it shares the cancellation token with the antecedent."); }, cancellationTokenSource.Token);
If you remove the token from the constructor then this continuation task will run after the antecedent has completed with an exception.
The following continuation task may seem correct at first as it should only run if the antecedent has been cancelled:
Task incorrectContinuation = motherTask.ContinueWith(antecedent => { Console.WriteLine("This task will never be scheduled"); }, cancellationTokenSource.Token, TaskContinuationOptions.OnlyOnCanceled, TaskScheduler.Current);
However, it also shares the same cancellation token as the antecedent, so it will never run. In other words if you want a continuation task to run even if the antecedent has been cancelled then do not share the same cancellation token across tasks.
The below task will run as it doesn’t share the cancellation token:
Task correctContinuation = motherTask.ContinueWith(antecedent => { Console.WriteLine("Continuation running as there's no cancellation token sharing."); }, TaskContinuationOptions.OnlyOnCanceled);
We cancel the token and wait for the correct continuation task to finish:
cancellationTokenSource.Cancel(); correctContinuation.Wait();
View the list of posts on the Task Parallel Library here.