2010-09-28 7 views
5

¡Buenos días! Supongamos que tenemos la siguiente clase:Implementación IDisposable para la clase que contiene hilos

class MultithreadOperation : IDisposable 
{ 
    private IList<Thread> operationThreads; 

    public void StartOperation() 
    { 
      // Initialize and start threads and put them to operationThreads 
    } 

    public void StopOperation() 
    { 
      // Aborts each thread. 
    } 

    public void Dispose() 
    { 
      Dispose(true); 
      GC.SuppressFinalize(this); 
    } 

    protected virtual void Dispose(bool disposing) 
    { 
     if (!disposed) 
     { 
       disposed = true; 
       if (disposing) 
       { 
        // Release managed resources. 
        #1: 
        StopOperation(); 
       } 
       // Release unmanaged resources. 
       #2: 
       StopOperation(); 
     } 
    } 

    ~MultithreadOperation() 
    { 
     Dispose(false); 
    } 
} 

En realidad, tengo que dejar todos los hilos, si se dispone de la instancia. Además, debo detener todos los hilos si la instancia es basura (de lo contrario, los hilos seguirán vivos, lo cual es malo para mí). Definitivamente, es totalmente legal invocar el método StopOperation() en el lugar # 1.

Quiero saber si hay algún inconveniente si invocamos StopOperation() en el lugar # 2? Según tengo entendido, la lista de subprocesos ya puede ser basura cuando se ejecuta ~ MultithreadOperation(). Además, he visto muchas recomendaciones para evitar cualquier código que se refiera a los recursos administrados en la implementación de Finalizar, especialmente los campos de instancia.

Además, sería interesante escuchar acerca de los diferentes enfoques para este problema. Gracias!

+0

posible duplicado de [C# Finalize/Dispose pattern] (http://stackoverflow.com/questions/898828/c-finalize-dispose-pattern) – x0n

+0

votación para cerrar. esto se ha preguntado y respondido un millón de veces en toda la web. reemplace "hilo" por "objeto gestionado" y la pregunta es un duplicado. – x0n

+0

¿Qué tal si el subproceso seguirá vivo después de que MutilthreadOperation es basura? Este comportamiento es bastante malo. El tema que refirió sugiere no detener el hilo en el finalizador. – Alexander

Respuesta

5

Esto es completamente inapropiado. Un usuario de su clase que no conoce bien los detalles asumirá que llamar a Dispose() hará algo inocente. Un poco de limpieza, nada lujoso. Ella va a no esperan que el programa se quede en un estado completamente impredecible con hilos que mutan ese estado abortado en ubicaciones aleatorias sin forma de recuperar el estado.

También es un redrum completo en el hilo del finalizador, abortar un hilo puede tomar varios segundos si el hilo no está en estado de alerta. El CLR abortará el hilo del finalizador después de 2 segundos y terminará el programa, sin dar ningún tipo de diagnóstico sobre la verdadera razón por la que se colapsó el programa.

+0

Gracias. Esto hace que la situación sea clara. – Alexander

+0

Supongo que estoy un poco tarde aquí, pero ¿qué ocurre cuando los hilos se eliminan limpiamente en StopOperation()? Además, ¿qué pasa con los casos donde no se llamó a Dispose? –

2

No es seguro para realizar una llamada a StopOperation en la posición # 2 porque:

  • El List de hilos o las Thread casos sí mismos pueden ya han sido recogidos.
  • No está claro si su StopOperation bloques hasta que todos los hilos hayan reconocido una señal de parada, pero si lo hace, entonces podría colgar la secuencia del finalizador.

Otro punto es que no debería estar abortando los hilos (a través de Thread.Abort) en este caso. La razón es porque el aborto inyecta una excepción fuera de banda (asíncrona) en el subproceso objetivo en puntos no deterministas. Debido a que estos puntos de inyección son aleatorios (ya menudo no intuitivos), pueden dañar el estado (rescatando a principios de una escritura u otra operación más compleja) en el dominio de la aplicación o incluso en todo el proceso.

La mejor manera de manejar esto es para hacer lo siguiente:

  • Tener la encuesta de hilo o escuchar una señal de parada. Puede ver my answer here para obtener consejos sobre cómo hacer esto.
  • Haga que la señal StopOperation sea cual sea el mecanismo de parada que elija. Por lo general, desea esperar hasta que todos los hilos hayan recibido la señal y el apagado por sí mismos. Puede esperar un hilo llamando al Thread.Join.
  • Llame al StopOperation en el método Dispose en la posición n. ° 1 solamente.

Ahora, ¿cómo tratamos con las personas que llaman que no juegan bien y evitamos llamar al Dispose? Esto es un poco más complicado. Veo dos patrones generales para lidiar con esto.

  • Haga que el finalizador alternar una bandera de parada. Esa bandera debe ser un tipo de valor; de lo contrario, estaría sujeta a la recolección de basura. Cada hilo debe sondear este indicador periódicamente. Eso significa que no debe realizar ninguna llamada de bloqueo que espere indefinidamente. Recuerde, no puede llamar al Thread.Interrupt para insertar un hilo, ya que requiere una referencia a una instancia de Thread.
  • Mantener una lista de subprocesos activos en una variable de recopilación estática de esa manera podría enumerarlos de forma segura en un finalizador. Sin embargo, debo admitir que estoy un poco herrumbroso con las reglas de recopilación de referencias estáticas durante la descarga del dominio de la aplicación (es decir, el cierre de la aplicación). Por lo tanto, no estoy seguro de que sea completamente seguro.
Cuestiones relacionadas