When to dispose CancellationTokenSource?
Speaking about whether it's really necessary to call Dispose on CancellationTokenSource
... I had a memory leak in my project and it turned out that CancellationTokenSource
was the problem.
My project has a service, that is constantly reading database and fires off different tasks, and I was passing linked cancellation tokens to my workers, so even after they had finished processing data, cancellation tokens weren't disposed, which caused a memory leak.
MSDN Cancellation in Managed Threads states it clearly:
Notice that you must call
Dispose
on the linked token source when you are done with it. For a more complete example, see How to: Listen for Multiple Cancellation Requests.
I used ContinueWith
in my implementation.
Correct pattern to dispose of cancellation token source
The correct practice is second - you dispose of the CancellationTokenSource
after you are sure the task is cancelled. CancellationToken
relies on information from CancellationTokenSource
to function properly. While the current implementation CancellationToken
is written in such a way that is will still work even without throwing exceptions if the CTS it was created from is disposed, it may not behave properly or always as expected.
How to implement cancellation and dispose a CancellationTokenSource correctly
It is not possible to tell where the error is originated from the code you have posted. Generally, you must inspect the eception message (callstack) to know where exactly the exception was triggered.
Call Dispose
once cancellation has been requested or the cancellable operations have completed. The exception is thrown when you access the mutating members of CancellationTokenSource
or its CancellationToken
instance, when the CancellationTokenSource
was exposed. Like when calling Cancel
on the disposed instance or trying to get the reference to the associated CancellationToken
after Dispose
was called. You must ensure that no code accesses the disposed instance.
You can do this by setting the CancellationTokenSource property to null when disposing and by adding a null check before accessing the CancellationTokenSource
. You must control the lifetime of the CancellationTokenSource
carefully.
The following example shows how to control the lifetime of the CancellationTokenSource
and guard against illegal referencing of the disposed instance:
private CancellationTokenSource CancellationtokenSource { get; set; }
private void CancelCancellableOperation_OnClick(object sender, EventArgs e)
{
// Check for null to avoid an ObjectDisposedException
// (or a NullReferenceException in particular) exception.
// The implemented pattern sets the property to null immediately after disposal (not thread-safe).
this.CancellationTokenSource?.Cancel();
}
// Start scope of CancellationTokenSource.
// Lifetime is managed by a try-catch-finally block and the use of
// CancellationToken.ThrowIfCancellationRequested
// to forcefully enter the try-catch-finally block on cancellation.
private async Task DoWorkAsync()
{
this.CancellationTokenSource = new CancellationTokenSource();
try
{
await CancellableOperationAsync(this.CancellationTokenSource.Token);
}
catch (OperationCanceledException)
{
// Do some cleanup or rollback.
// At this point the CancellationTokenSource is still not disposed.
}
finally
{
// Invalidate CancellationTokenSource property to raise an NullReferenceException exception
// to indicate that thet access ocurred uncontrolled and requires a fix.
// Create a local copy of the property to avoid race conditions.
var cancellationTokenSource = this.CancellationTokenSource;
this.CancellationTokenSource = null;
// Dispose after cancellation
// or cancellable operations are completed
cancellationTokenSource.Dispose();
}
}
private async Task CancellableOperationAsync(CancellationToken cancellationToken)
{
// Guarantee that CancellationTokenSource is never disposed before
// CancellationTokenSource.Cancel was called or the cancellable operation has completed
// Do something
while (true)
{
await Task.Delay(TimeSpan.FromSeconds(10));
// Add null check if you can't guard against premature disposal
cancellationToken?.ThrowIfCancellationRequested();
}
}
is it necessary to cancel cancellationtoken before disposal
After a CancellationTokenSource
has been disposed tokens based on this source may throw ObjectDisposedException
so you should not use CancellationTokenSource.Token
after the source has been disposed. Fortunately, I don't see this happening in your code.
When you cancel a CancellationTokenSource
it changes state and notifies callbacks that have been registered for the token. However, when your code is about to dispose the CancellationTokenSource
you are already done using the token and there is no need to cancel it.
So in your case it is not necessary to cancel the CancellationTokenSource
before disposing it. However, your use case is somewhat special. When you have a background task you should wait for the task to complete before disposing the source (as stated in my initial paragraph):
using (var cts = new CancellationTokenSource()) {
var task = Task.Run(() => DoSomething(cts.Token));
// Cancel cts or let it cancel itself based on a timeout.
// Then wait for the task to end.
await task;
}
Calling cancellationToken.Cancel() in Dispose of Controller?
Does disposing the Cancellation token in Controller.Dispose() causes
the long running task to cancel?
Depends on how your longRunningTask
was implemented.
In this method you should explicitly check whether the cancellation is requested:
token.ThrowIfCancellationRequested();
After invoking of this method your task will be cancelled.
Cancellation example
If in the following example the ThrowIfCancellationRequested
would not be invoked, the task would be run forever:
var cts = new CancellationTokenSource();
Task.Run(() =>
{
while (true)
cts.Token.ThrowIfCancellationRequested();
}, cts.Token);
You can learn more about cancellation here.
Note that after setting the cancellationTokenSource
to null
, you can get NullReferenceException
in your foreach
loop. I would suggest to copy your token into a local variable:
public async Task<HttpResponseMessage> Post(SomeData data)
{
var token = cancellationTokenSource.Token;
foreach (var item in data)
{
await longRunningTask(item, token);
}
}
Related Topics
Typeloadexception Says 'No Implementation', But It Is Implemented
Way to Have String.Replace Only Hit "Whole Words"
How to Use Webrequest to Access an Ssl Encrypted Site Using Https
Regex for Accepting Only Persian Characters
In C#, How to Check If a Tcp Port Is Available
How to Get the Exif Data from a File Using C#
Dependency Injection Unity - Conditional Resolving
Configuration System Failed to Initialize
How to Call Functions in a Main View Model from Other View Models
At the End of an Async Method, Should I Return or Await
Interprocess Communication for Windows in C# (.Net 2.0)
Most Efficient Way to Remove Special Characters from String
Convert an Image (Selected by Path) to Base64 String
Can Console.Clear Be Used to Only Clear a Line Instead of Whole Console