¿Se considera aceptable no invocar a Dispose () en un objeto TPL Task?

Quiero activar una tarea para ejecutar en una cadena de fondo. No quiero esperar para completar las tareas.

En .net 3.5, habría hecho esto:

ThreadPool.QueueUserWorkItem(d => { DoSomething(); }); 

En .net 4, el TPL es el camino sugerido. El patrón común que he visto recomendado es:

 Task.Factory.StartNew(() => { DoSomething(); }); 

Sin embargo, el método StartNew() devuelve un objeto Task que implementa IDisposable . Esto parece ser pasado por alto por las personas que recomiendan este patrón. La documentación de MSDN en el método Task.Dispose() dice:

“Siempre llame a Dispose antes de lanzar su última referencia a la Tarea”.

No se puede invocar a deshacerse de una tarea hasta que se complete, por lo que esperar a que el hilo principal espere y deshacerse de las llamadas derrotaría el punto de vista en una cadena de fondo en primer lugar. Tampoco parece haber ningún evento completado / terminado que pueda usarse para la limpieza.

La página MSDN en la clase Tarea no hace ningún comentario al respecto, y el libro “Pro C # 2010 …” recomienda el mismo patrón y no hace comentarios sobre la eliminación de tareas.

Sé que si lo dejo, el finalizador lo captará al final, pero ¿esto va a volver y morderme cuando estoy haciendo mucho fuego y olvidarme de tareas como esta y el hilo finalizador se ve abrumado?

Entonces mis preguntas son:

  • ¿Es aceptable no llamar a Dispose() en la clase Task en este caso? Y si es así, ¿por qué y hay riesgos / consecuencias?
  • ¿Hay alguna documentación que discuta esto?
  • ¿O hay una forma adecuada de deshacerse del objeto Task que me he perdido?
  • ¿O hay otra forma de disparar y olvidar tareas con el TPL?

Hay una discusión sobre esto en los foros de MSDN .

Stephen Toub, miembro del equipo pfx de Microsoft, tiene esto que decir:

Tarea. La existencia de un objeto se debe a una tarea que potencialmente envuelve un identificador de evento utilizado cuando se espera que la tarea se complete, en caso de que el hilo en espera realmente tenga que bloquearse (en lugar de girar o ejecutar potencialmente la tarea que está esperando). Si todo lo que hace es usar continuaciones, ese identificador de evento nunca se asignará

es probable que sea mejor confiar en la finalización para encargarse de las cosas.

Actualización (oct 2012)
Stephen Toub ha publicado un blog titulado ¿Debo deshacerme de las tareas? que proporciona más detalles y explica las mejoras en .Net 4.5.

En resumen: no necesita deshacerse de los objetos Task 99% del tiempo.

Hay dos razones principales para deshacerse de un objeto: liberar recursos no administrados de manera oportuna y determinista, y evitar el costo de ejecutar el finalizador del objeto. Ninguno de estos se aplica a la Task mayor parte del tiempo:

  1. A partir de .Net 4.5, la única vez que una Task asigna el identificador de espera interno (el único recurso no administrado en el objeto Task ) es cuando usted usa explícitamente el IAsyncResult.AsyncWaitHandle de la Task , y
  2. El objeto Task sí no tiene un finalizador; el identificador está envuelto en un objeto con un finalizador, por lo tanto, a menos que esté asignado, no hay un finalizador para ejecutar.

Este es el mismo tipo de problema que con la clase Thread. Consume 5 identificadores del sistema operativo pero no implementa IDisposable. Una buena decisión de los diseñadores originales, por supuesto, hay pocas formas razonables de llamar al método Dispose (). Tendría que llamar a Join () primero.

La clase Tarea agrega un identificador a esto, un evento de reinicio manual interno. ¿Cuál es el recurso de sistema operativo más barato que existe? Por supuesto, su método Dispose () solo puede liberar ese identificador de evento, no los 5 identificadores que consume Thread. Sí, no te molestes .

Tenga cuidado de no estar interesado en la propiedad IsFaulted de la tarea. Es un tema bastante feo, puede leer más sobre esto en este artículo de MSDN Library . Una vez que lidie con esto correctamente, también debe tener un buen lugar en su código para deshacerse de las tareas.

Me encantaría ver a alguien sopesar la técnica que se muestra en esta publicación: invocación de delegado asincrónica tipo fuego fácil de olvidar en C #

Parece que un método de extensión simple manejará todos los casos triviales de interacción con las tareas y podrá realizar una llamada a deshacerse de él.

 public static void FireAndForget(this Action act,T arg1) { var tsk = Task.Factory.StartNew( ()=> act(arg1), TaskCreationOptions.LongRunning); tsk.ContinueWith(cnt => cnt.Dispose()); }