Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

488
Vistas
Fuga de memoria al disparar y olvidar Task.Delay con CancellationToken

En mi aplicación, necesito programar la ejecución de algunas Action con retraso. Es como setTimeout en JavaScript. Además, cuando finaliza la ejecución de la aplicación, necesito cancelar todas las ejecuciones programadas que aún no se han ejecutado. Así que tengo que llamar a Task.Delay sin esperar y pasarle CancellationToken . Pero si lo hago, me enfrento a una fuga de memoria: ninguno de CancellationTokenSource+CallbackNode se desechará hasta que llame a Cancel y Dispose de CancellationTokenSource desde donde tomo CancellationToken s para pasar a Task.Delay .

Ejemplo mínimo reproducible:

 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();

Después de ejecutar este ejemplo, deja 1000 de CancellationTokenSource+CallbackNode . con fuga

Si escribo cts.Cancel() después de await Task.Delay(1000); la fuga no aparece sin fugas

¿Por qué ocurre esta fuga? Todas las Task se completaron, por lo que no debería haber referencias a cts.Token . Deshacerse de Task pasada a la acción de continuación no ayuda.
Además, si await una tarea que programe la ejecución de la acción, no aparece la fuga.

over 4 years ago · Santiago Trujillo
1 Respuestas
Responde la pregunta

0

Me sorprendió un poco, pero parece que esto es a propósito. Cuando se cancela un registro, CancellationTokenSource aún mantiene la instancia de CancellationTokenSource+CallbackNode en una lista libre para reutilizarla para la próxima devolución de llamada. Esta es una optimización de opinión, que puede ser contraproducente en su escenario.

Para ilustrar esto, intente ejecutar su prueba dos veces seguidas:

 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()

Verá que todavía hay 1000 instancias de CancellationTokenSource+CallbackNode , a pesar de registrar 2000 devoluciones de llamada en total. Eso es porque la segunda iteración reutilizó los nodos creados durante la primera.

No hay mucho que puedas hacer al respecto, creo que esto es por diseño. En cualquier caso, la cantidad de memoria debería ser prácticamente insignificante (como máximo x instancias, donde x es el número de devoluciones de llamada registradas simultáneamente).

over 4 years ago · Santiago Trujillo Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda