In my application I need to schedule executing of some Action
s with delay. It's like setTimeout
in JavaScript. Also, when app execution ends I need to cancel all scheduled executions that have not been executed yet. So I have to call Task.Delay
without await and pass CancellationToken
into it. But if I do so, I face with memory leak: none of CancellationTokenSource+CallbackNode
will be disposed until I call Cancel
and Dispose
of CancellationTokenSource
from which I take CancellationToken
s to pass to Task.Delay
.
Minimal reproducible example:
CancellationTokenSource cts = new CancellationTokenSource();
for (int i = 0; i < 1000; i++)
{
Task.Delay(500, cts.Token).ContinueWith(_ => Console.WriteLine("Scheduled action"));
}
await Task.Delay(1000);
Console.ReadLine();
After executing this example, it leave 1000 of CancellationTokenSource+CallbackNode
.
If I write cts.Cancel()
after await Task.Delay(1000);
leak does not appear
Why this leak happens? All Task
s was completed, so there should not be references to cts.Token
. Disposing passed Task
to continuation action does not help.
Also, if I await
Task that schedule execution of action, leak does not appear.
GC.Collect()
before theConsole.ReadLine()
? – DiffusiveTask.Delay
creates a hot task. Awaiting the task is certainly not a requirement for that task getting scheduled. You might confusing it with theAsyncLazy<T>
type. – DiffusiveGC.Collect()
before theConsole.ReadLine()
does not make any difference. – Fulcher