Empresas
Empregos
  • Sobre nós
  • Soluções
    • Publicação de vagas
      Publique sua vaga e receba candidatos qualificados em 48h.
    • Avaliações de candidatos
      Mais de 500 testes técnicos e psicológicos, mais anti-fraude.
    • Headhunting
      Busca executiva personalizada do início ao fim.
    • Folha de Pagamento + EOR
      Dispersão de folha e EOR em mais de 15 países da LATAM.
  • Preços
  • Empregos

0

486
Visualizações
Memory leak when doing fire-and-forget of Task.Delay with CancellationToken

In my application I need to schedule executing of some Actions 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 CancellationTokens 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. with leak

If I write cts.Cancel() after await Task.Delay(1000); leak does not appear without leak

Why this leak happens? All Tasks 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.

over 4 years ago · Santiago Trujillo
1 Respostas
Responde à pergunta

0

I was a bit surprised, but it looks like this is on purpose. When a registration is cancelled, CancellationTokenSource still keeps the instance of CancellationTokenSource+CallbackNode in a free-list to reuse it for the next callback. This is an opiniated optimization, that can backfire in your scenario.

To illustrate this, try running your test twice in a row:

var tasks = new Task[1000];

for (int i = 0; i < 1000; i++)
{
    tasks[i] = Task.Delay(500, cts.Token).ContinueWith(_ => Console.WriteLine("Scheduled action"));
}
await Task.WhenAll(tasks);

for (int i = 0; i < 1000; i++)
{
    tasks[i] = Task.Delay(500, cts.Token).ContinueWith(_ => Console.WriteLine("Scheduled action"));
}
await Task.WhenAll(tasks);

Console.ReadLine()

You will see that there are still 1000 instances of CancellationTokenSource+CallbackNode, despite registering 2000 callbacks in total. That's because the second iteration reused the nodes created during the first one.

Not much you can do about this, I believe this is by design. In any case, the amount of memory should be mostly negligible (at most x instances, where x is the number of simultaneously registered callbacks).

over 4 years ago · Santiago Trujillo Relatório
Responde à pergunta
Encontrar trabalhos remotos

Descubra a nova forma de encontrar um emprego!

melhores empregos
Principais categorias de trabalho
Empresas
Postar vaga Preços Comercial
Jurídico
Termos e Condições Política de privacidade
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomende algumas ofertas para mim
Preciso de ajuda