Cancellation token in Task constructor: why?
Certain System.Threading.Tasks.Task
constructors take a CancellationToken
as a parameter:
CancellationTokenSource source = new CancellationTokenSource();
Task t = new Task (/* method */, source.Token);
What baffles me about this is that there is no way from inside the method body to actually get at the token passed in (e.g., nothing like Task.CurrentTask.CancellationToken
). The token has to be provided through some other mechanism, such as the state object or captured in a lambda.
So what purpose does providing the cancellation token in the constructor serve?
Solution 1:
Passing a CancellationToken
into the Task
constructor associates it with the task.
Quoting Stephen Toub's answer from MSDN:
This has two primary benefits:
- If the token has cancellation requested prior to the
Task
starting to execute, theTask
won't execute. Rather than transitioning toRunning
, it'll immediately transition toCanceled
. This avoids the costs of running the task if it would just be canceled while running anyway.- If the body of the task is also monitoring the cancellation token and throws an
OperationCanceledException
containing that token (which is whatThrowIfCancellationRequested
does), then when the task sees thatOperationCanceledException
, it checks whether theOperationCanceledException
's token matches the Task's token. If it does, that exception is viewed as an acknowledgement of cooperative cancellation and theTask
transitions to theCanceled
state (rather than theFaulted
state).
Solution 2:
The constructor uses the token for cancellation handling internally. If your code would like access to the token you are responsible for passing it to yourself. I would highly recommend reading the Parallel Programming with Microsoft .NET book at CodePlex.
Example usage of CTS from the book:
CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken token = cts.Token;
Task myTask = Task.Factory.StartNew(() =>
{
for (...)
{
token.ThrowIfCancellationRequested();
// Body of for loop.
}
}, token);
// ... elsewhere ...
cts.Cancel();