Pregunta

Tengo una clase llamada BackgroundWorker que tiene un hilo corriendo constantemente. Para desactivar este subproceso, una variable de instancia denominada stop to debe ser true .

Para asegurarme de que el hilo se libera cuando se termina de usar la clase, agregué IDisposable y un finalizador que invoca Dispose () . Suponiendo que stop = true realmente hace que este hilo salga, ¿es correcto este sippet? Está bien invocar Dispose desde un finalizador, ¿verdad?

Los finalizadores siempre deben llamar a Dispose si el objeto hereda IDisposable , ¿verdad?

/// <summary>
/// Force the background thread to exit.
/// </summary>
public void Dispose()
{
    lock (this.locker)
    {
        this.stop = true;
    }
}

~BackgroundWorker()
{
    this.Dispose();
}
¿Fue útil?

Solución

Su código está bien, aunque bloquearlo en un finalizador es algo '' aterrador '' y lo evitaría, si tienes un punto muerto ... No estoy 100% seguro de lo que sucederá, pero no sería bueno. Sin embargo, si está seguro, esto no debería ser un problema. Principalmente. Los aspectos internos de la recolección de basura son dolorosos y espero que nunca tengas que verlos;)

Como señala Marc Gravell, un bool volátil le permitiría deshacerse del bloqueo, lo que mitigaría este problema. Implemente este cambio si puede.

El código de

nedruod coloca la asignación dentro de la verificación if (eliminación), lo cual es completamente incorrecto: el subproceso es un recurso no administrado y debe detenerse incluso si no se elimina explícitamente. Su código está bien, solo estoy señalando que no debe seguir los consejos dados en ese fragmento de código.

Sí, casi siempre debe llamar a Dispose () desde el finalizador si implementa el patrón IDisposable. El patrón IDisposable completo es un poco más grande que el que tiene, pero no siempre lo necesita, simplemente ofrece dos posibilidades adicionales:

  1. detectar si se llamó a Dispose () o si se está ejecutando el finalizador (no se le permite tocar ningún recurso administrado en el finalizador, fuera del objeto que se está finalizando);
  2. habilitar subclases para anular el método Dispose ().

Otros consejos

Primero, una advertencia severa . No use un finalizador como usted. Te estás preparando para algunos efectos muy malos si tomas bloqueos dentro de un finalizador. La historia corta es no hacerlo. Ahora a la pregunta original.

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

/// <summary>
/// Force the background thread to exit.
/// </summary>
protected virtual void Dispose(bool disposing)
{
    if (disposing)
    {
        lock (this.locker)
        {
            this.stop = true;
        }
    }
}

~BackgroundWorker()
{
    Dispose(false);
}

La única razón para tener un finalizador es permitir que las subclases extiendan y liberen recursos no administrados . Si no tiene subclases, selle su clase y suelte el finalizador por completo.

Fuera de interés, cualquier razón por la cual esto no podría usar el BackgroundWorker , que tiene soporte completo para la cancelación?

Vuelva a bloquear: un campo bool volátil podría ser menos problemático.

Sin embargo, en este caso su finalizador no está haciendo nada interesante, especialmente dado el " if (disposición) " - es decir, solo ejecuta el código interesante durante Dispose (). Personalmente, me sentiría tentado a seguir con solo IDisposable, y no proporcionar un finalizador: debería limpiarlo con Dispose ().

Es el " stop " variable de instancia una propiedad? Si no es así, no hay ningún punto particular en configurarlo durante el finalizador: ya nada hace referencia al objeto, por lo que nada puede consultar al miembro.

Si realmente está liberando un recurso, entonces hacer que Dispose () y el finalizador realicen el mismo trabajo (primero probar si el trabajo aún debe realizarse) es un buen patrón.

Necesita el patrón desechable completo, pero la parada debe ser algo a lo que pueda acceder el hilo. Si es una variable miembro de la clase que se está eliminando, eso no es bueno porque no puede hacer referencia a una clase eliminada. Considere tener un evento que posea el subproceso y señalizar que en lugar de deshacerse.

El objeto que implementa el finalizador necesita una referencia a un indicador, almacenado en otro objeto , que el hilo podrá ver; el subproceso no debe tener una referencia fuerte, directa o indirecta, al objeto que implementa el finalizador. El finalizador debe establecer la bandera usando algo como un CompareExchange, y el hilo debe usar un medio similar para probarlo. Tenga en cuenta que si el finalizador de un objeto accede a otro objeto, el otro objeto puede haberse finalizado pero seguirá existiendo. Está bien que un finalizador haga referencia a otros objetos si lo hace de una manera que no sea molestada por su finalización. Si todo lo que está haciendo es establecer una bandera, está bien.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top