Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why CancellationToken is separate from CancellationTokenSource?

People also ask

What is difference between CancellationTokenSource and CancellationToken?

CancellationTokenSource – an object responsible for creating a cancellation token and sending a cancellation request to all copies of that token. CancellationToken – a structure used by listeners to monitor token current state.

What is the purpose of CancellationToken?

A CancellationToken enables cooperative cancellation between threads, thread pool work items, or Task objects. You create a cancellation token by instantiating a CancellationTokenSource object, which manages cancellation tokens retrieved from its CancellationTokenSource. Token property.

Does CancellationTokenSource need to be disposed?

Always call Dispose before you release your last reference to the CancellationTokenSource. Otherwise, the resources it is using will not be freed until the garbage collector calls the CancellationTokenSource object's Finalize method.

What happens when a CancellationToken is Cancelled?

Remarks. The associated CancellationToken will be notified of the cancellation and will transition to a state where IsCancellationRequested returns true. Any callbacks or cancelable operations registered with the CancellationToken will be executed.


I was involved in the design and implementation of these classes.

The short answer is "separation of concerns". It is quite true that there are various implementation strategies and that some are simpler at least regarding the type system and initial learning. However, CTS and CT are intended for use in a great many scenarios (such as deep library stacks, parallel computation, async, etc) and thus was designed with many complex use cases in mind. It is a design intended to encourage successful patterns and discourage anti-patterns without sacrificing performance.

If the door was left open for misbehaving APIs, then the usefulness of the cancellation design could quickly be eroded.

CancellationTokenSource == "cancellation trigger", plus generates linked listeners

CancellationToken == "cancellation listener"


I had the exact question and I wanted to understand the rationale behind this design.

The accepted answer got the rationale exactly right. Here's the confirmation from the team who designed this feature (emphasis mine):

Two new types form the basis of the framework: A CancellationToken is a struct that represents a ‘potential request for cancellation’. This struct is passed into method calls as a parameter and the method can poll on it or register a callback to be fired when cancellation is requested. A CancellationTokenSource is a class that provides the mechanism for initiating a cancellation request and it has a Token property for obtaining an associated token. It would have been natural to combine these two classes into one, but this design allows the two key operations (initiating a cancellation request vs. observing and responding to cancellation) to be cleanly separated. In particular, methods that take only a CancellationToken can observe a cancellation request but cannot initiate one.

Link: .NET 4 Cancellation Framework

In my opinion, the fact that CancellationToken can only observe the state and not change it, is extremely critical. You can hand out the token like a candy and never worry that someone else, other than you, will cancel it. It protects you from hostile third party code. Yes, the chances are slim, but I personally like that guarantee.

I also feel that it makes the API cleaner and avoids accidental mistake and promotes better component design.

Let's look at public API for both of these classes.

CancellationToken API

CancellationTokenSource API

If you were to combine them, when writing LongRunningFunction, I will see methods like those multiple overloads of 'Cancel' which I should not be using. Personally, I hate to see Dispose method as well.

I think the current class design follows 'pit of success' philosophy, it guides developers to create better components which can handle Task cancellation and then instrument them together in numerous way to create complicated workflows.

Let me ask you a question, have you wondered what is the purpose of token.Register? It didn't make sense to me. And then I read Cancellation in Managed Threads and everything became crystal clear.

I believe that the Cancellation Framework Design in TPL is absolutely top notch.


They are separate not for technical reasons but semantic ones. If you look at the implementation of CancellationToken under ILSpy, you'll find it's merely a wrapper around CancellationTokenSource (and thus no different performance-wise than passing around a reference).

They provide this separation of functionality to make things more predictable: when you pass a method a CancellationToken, you know you're still the only one that can cancel it. Sure, the method could still throw a TaskCancelledException, but the CancellationToken itself -- and any other methods referencing the same token -- would remain safe.


The CancellationToken is a struct so many copies could exist due to passing it along to methods.

The CancellationTokenSource sets the state of ALL copies of a token when calling Cancel on the source. See this MSDN page

The reason for the design might be just a matter of separation of concerns and the speed of a struct.