2011-12-21 13 views
5

Estoy revisando un código de ejemplo en un libro y encontré el siguiente código (simplificado). En el código, cuando se invoca Subscribe(T subscriber), el hilo entra en una sección de bloqueo. y luego, cuando el código dentro del bloqueo llama al método AddToSubscribers(T subscriber), el método tiene otro bloqueo. ¿Por qué es necesario este segundo bloqueo?razonamiento detrás de la cerradura dentro de la cerradura?

public abstract class SubscriptionManager<T> where T : class 
{ 
    private static List<T> subscribers; 
    private static void AddToSubscribers(T subscriber) 
    { 
     lock (typeof(SubscriptionManager<T>)) 
     { 
     if (subscribers.Contains(subscriber)) 
      return; 
     subscribers.Add(subscriber); 
     } 
    } 

    public void Subscribe(T subscriber) 
    { 
     lock (typeof(SubscriptionManager<T>)) 
     { 
     AddToSubscribers(subscriber); 
     } 
    } 
} 

Respuesta

10

En ese contexto, no lo es; sin embargo, dado que los bloqueos son reentrantes, pueden ser útiles para garantizar que cualquier otra persona que llama de AddToSubscribers observe el bloqueo. En realidad, por esa razón yo diría "quítenlo de Subscribe y simplemente dejen que AddToSubscribers hagan el bloqueo".

¡Sin embargo! Un bloqueo en un Type es bastante peligroso. Un campo sería más seguro:

// assuming static is correct 
private static readonly object syncLock = new object(); 

y lock(syncLock). Dependiendo de cuándo se asigne subscribers, también puede salirse con lock(subscribers) (y sin campo adicional).

que debería tener en cuenta también que un métodoejemplo añadir a estática estado es bastante inusual ....; IMO Subscribe debe ser un método static, ya que no tiene nada que ver con la instancia actual.

+0

en realidad, este ejemplo proviene de un libro WCF, y la clase SubscriptionManager es la clase base para el servicio WCF clases por lo tanto, se requieren los métodos de instancia. También leí en MSDN que el bloqueo en un Tipo es peligroso, como dijiste, ¿el hecho de que el contexto sea WCF hace alguna diferencia? – Yeonho

+1

@Daniel ninguno en absoluto. –

+0

@Marc "private static readonly object syncLock = new object();" - esto le da un único bloqueo global para todos los SubscriptionManager ; mientras que creo que el código original tiene un bloqueo por tipo construido (es decir, uno para cada "T" distinta). – Joe

4

En el código que ha publicado, no es necesario. Pero, una vez más, el código que publicaste está incompleto; por ejemplo, la lista de suscriptores nunca se inicializa.

El bloqueo en typeof (SubscriptionManager) tampoco es una buena idea, ya que bloquear el campo subscribers sería mejor, pero sería necesario inicializar el campo de suscriptores, p.

private static List<T> subscribers = new List<T>(); 
2

Probablemente deberías leer cerca de esa muestra y ver de qué se trata el libro.

Para ese caso en particular, no, el segundo bloqueo es innecesario.

Nota: La muestra es peligrosa ya que se bloquea en un objeto público (tipo). Normalmente, uno se bloquea en un objeto privado especial, por lo que el código externo no puede introducir errores por error al bloquear erróneamente el mismo objeto.

1

También me enfrenté a una situación en la que tuve que usar el bloqueo anidado.

Mi caso fue que la función de la segunda cerradura puede ser llamada desde otro lugar, ya que era una función estática. Sin embargo, para su caso no será necesario ya que cada miembro de datos pertenece a una instancia y no estático.

Cuestiones relacionadas