tasks - thread c#
¿Se considera aceptable no invocar a Dispose() en un objeto TPL Task? (3)
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.
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 claseTask
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:
- 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 objetoTask
) es cuando usted usa explícitamente elIAsyncResult.AsyncWaitHandle
de laTask
, y - 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.
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<T>(this Action<T> act,T arg1)
{
var tsk = Task.Factory.StartNew( ()=> act(arg1),
TaskCreationOptions.LongRunning);
tsk.ContinueWith(cnt => cnt.Dispose());
}