I have a need for an application with one global CancellationTokenSource
. This CancellationTokenSource
is to be triggered only when the user closes the app.
The app ‘does work’ and for each work item I want to pass in a CancellationToken
from the global CancellationTokenSource
. The idea is that when the user
wants to exit the app, any running work items that they started will be cancelled before the app closes. The question is, how can I implement this without
creating a memory leak. Below is a sample of this idea that seems to be leaking memory:
internal class Program
{
private static CancellationTokenSource cts = new CancellationTokenSource();
static async Task Main(string[] args)
{
while (StartAnotherWorkItem())
{
Task.Run(DoWork, cts.Token);
}
}
static async Task DoWork()
{
var dictionary = string.Empty;
for (int i = 0; i < 100_000_000; i++)
{
dictionary += "SomeLongString";
}
}
static bool StartAnotherWorkItem()
{
Console.WriteLine("Start another work item? [Y/N]");
var response = Console.ReadKey();
if (response.KeyChar == 'y' || response.KeyChar == 'Y') return true;
return false;
}
}
And out of curiosity, would this be due to the same issue as mentioned here:
BAD This example uses Task.Delay(-1, token) to create a Task that completes when the CancellationToken fires, but if it doesn’t fire, there’s no way to dispose of the CancellationTokenRegistration created inside of Task.Delay. This can lead to a memory leak.
public static async Task<T> WithCancellation<T>(this Task<T> task, CancellationToken cancellationToken)
{
// There's no way to dispose of the registration
var delayTask = Task.Delay(-1, cancellationToken);
var resultTask = await Task.WhenAny(task, delayTask);
if (resultTask == delayTask)
{
// Operation cancelled
throw new OperationCanceledException();
}
return await task;
}