Consider a scenario where you have some asynchronous work to be done and you can run it in a fire and forget mode. This asynchronous work is able to listen for cancellation and so you pass it a cancellation token in order to being able to cancel it.
At a given moment in time we can decide to request the cancellation of the ongoing activity, by using the cancellation token source object from which we have taken the cancellation token.
Because cancellation token source implements IDisposable
, we should call its Dispose
method as far as we are done with it. The point of this question is determining exactly when you are done with a given cancellation token source.
Let's suppose that you decide to cancel the ongoing work by calling the Cancel
method on the cancellation token source: is it necessary to wait for the completion of the ongoing operation before calling Dispose
?
Put in other words, should I do this way:
class Program
{
static void Main(string[] args)
{
var cts = new CancellationTokenSource();
var token = cts.Token;
DoSomeAsyncWork(token); // starts the asynchronous work in a fire and forget manner
// do some other stuff here
cts.Cancel();
cts.Dispose(); // I call Dispose immediately after cancelling without waiting for the completion of ongoing work listening to the cancellation requests via the token
// do some other stuff here not involving the cancellation token source because it's disposed
}
async static Task DoSomeAsyncWork(CancellationToken token)
{
await Task.Delay(5000, token).ConfigureAwait(false);
}
}
or this way:
class Program
{
static async Task Main(string[] args)
{
var cts = new CancellationTokenSource();
var token = cts.Token;
var task = DoSomeAsyncWork(token); // starts the asynchronous work in a fire and forget manner
// do some other stuff here
cts.Cancel();
try
{
await task.ConfigureAwait(false);
}
catch(OperationCanceledException)
{
// this exception is raised by design by the cancellation
}
catch (Exception)
{
// an error has occurred in the asynchronous work before cancellation was requested
}
cts.Dispose(); // I call Dispose only when I'm sure that the ongoing work has completed
// do some other stuff here not involving the cancellation token source because it's disposed
}
async static Task DoSomeAsyncWork(CancellationToken token)
{
await Task.Delay(5000, token).ConfigureAwait(false);
}
}
Additional details: the code I'm referring to is written inside an ASP.NET core 2.2 web application, here I'm using a console application scenario just to simplify my example.
I've found similar questions on stackoverflow asking for the need to dispose of cancellation token sources objects. Some of the answers suggest that in some scenario disposing of this object is not really needed.
My approach to the whole IDisposable
subject is that I always tend to adhere to the exposed contract of a class, put another way if an object claims to be disposable I prefer to always call Dispose
when I'm done with it. I don't like the idea of guessing whether or not calling dispose is really required by depending on implementation details of the class that could change in a future release in an undocumented manner.