2008-10-23 18 views
29

Estoy usando C# & .NEt 3.5. ¿Cuál es la diferencia entre OptionA y OptionB?Diferencia entre bloqueo (taquilla) y bloqueo (variable_which_I_am_uso)

class MyClass 
{ 
    private object m_Locker = new object(); 
    private Dicionary<string, object> m_Hash = new Dictionary<string, object>(); 

    public void OptionA() 
    { 
     lock(m_Locker){ 
      // Do something with the dictionary 
     } 
    } 

    public void OptionB() 
    { 
     lock(m_Hash){ 
      // Do something with the dictionary 
     } 
    }  
} 

estoy empezando a incursionar en el roscado (primariamente para crear una memoria caché para una aplicación multi-hilo, NO utilizar la clase HttpCache, ya que no está unido a un sitio web), y veo la sintaxis optiona en muchos de los ejemplos que veo en línea, pero no entiendo qué razón, si es que hay alguno, se hace sobre OptionB.

Respuesta

27

La opción B usa el objeto que se debe proteger para crear una sección crítica. En algunos casos, esto comunica más claramente la intención. Si se usa constantemente, garantiza sólo una sección crítica para el objeto protegido estará activo a la vez:

lock (m_Hash) 
{ 
    // Across all threads, I can be in one and only one of these two blocks 
    // Do something with the dictionary 
} 
lock (m_Hash) 
{ 
    // Across all threads, I can be in one and only one of these two blocks 
    // Do something with the dictionary 
} 

Opción A es menos restrictiva. Utiliza un objeto secundario para crear una sección crítica para el objeto a proteger. Si se utilizan múltiples objetos secundarios, es posible tener más de una sección crítica activa para el objeto protegido a la vez.

private object m_LockerA = new object(); 
private object m_LockerB = new object(); 

lock (m_LockerA) 
{ 
    // It's possible this block is active in one thread 
    // while the block below is active in another 
    // Do something with the dictionary 
} 
lock (m_LockerB) 
{ 
    // It's possible this block is active in one thread 
    // while the block above is active in another 
    // Do something with the dictionary 
} 

La opción A es equivalente a la opción B si utiliza solo un objeto secundario. En cuanto a la lectura del código, la intención de la Opción B es más clara. Si está protegiendo más de un objeto, la opción B no es realmente una opción.

3

No es lo que está "Bloqueando", es el código que está contenido entre la cerradura {...} que es importante y que impide que se ejecute.

Si un hilo saca un candado() en cualquier objeto, impide que otros hilos obtengan un candado en el mismo objeto, y por lo tanto evita que el segundo hilo ejecute el código entre los paréntesis.

Es por eso que la mayoría de la gente crea un objeto basura para bloquear, evita que otros subprocesos obtengan un bloqueo en ese mismo objeto no deseado.

+1

No veo cómo el segundo y el tercer párrafo siguen al primero, que afirma que lo que está bloqueando no es importante. –

1

Bueno, depende de lo que haya querido bloquear (se hará seguro para los hilos).

Normalmente, elegiría la opción B para proporcionar acceso en modo hilo a m_Hash SOLAMENTE. Donde como OptionA, usaría para bloquear el tipo de valor, que no se puede usar con el bloqueo, o tengo un grupo de objetos que necesitan bloquearse al mismo tiempo, pero no sé cómo bloquear la instancia completa usando lock(this)

0

Bloquear el objeto que está utilizando es simplemente una cuestión de conveniencia. Un objeto de bloqueo externo puede simplificar las cosas, y también es necesario si el recurso compartido es privado, como con una colección (en cuyo caso utiliza el objeto ICollection.SyncRoot).

10

Es importante comprender que el bloqueo (m_Hash) hace NO evitar que otro código use el hash. Solo evita que se ejecute otro código que también utiliza m_Hash como su objeto de bloqueo.

Una de las razones para utilizar la opción A es que es probable que las clases tengan variables privadas que usará dentro de la instrucción de bloqueo. Es mucho más fácil simplemente usar un objeto que use para bloquear el acceso a todos ellos en lugar de tratar de usar bloqueos de grano más finos para bloquear el acceso solo a los miembros que necesitará. Si intenta usar el método de grano más fino, probablemente tendrá que realizar varios bloqueos en algunas situaciones y luego deberá asegurarse de tomarlos siempre en el mismo orden para evitar interbloqueos.

Otra razón para usar la opción A es porque es posible que la referencia a m_Hash sea accesible fuera de su clase. Quizás tenga una propiedad pública que le proporcione acceso, o tal vez la declare como protegida y las clases derivadas puedan usarla. En cualquier caso, una vez que el código externo tiene una referencia, es posible que el código externo lo use para un bloqueo. Esto también abre la posibilidad de interbloqueos ya que no tiene forma de controlar o saber qué orden tomará el bloqueo.

2

Creo que el alcance de la variable que "transfiere" determinará el alcance del bloqueo. es decir, una variable de instancia será con respecto a la instancia de la clase, mientras que una variable estática será para todo el dominio de la aplicación.

Al observar la implementación de las colecciones (utilizando Reflector), el patrón parece seguir que una variable de instancia llamada SyncRoot se declara y usa para todas las operaciones de bloqueo con respecto a la instancia de la colección.

7

En realidad, no es buena idea bloquear el objeto si está utilizando sus miembros. Jeffrey Richter escribió en su libro "CLR a través de C#" que no hay garantía de que una clase de objeto que está utilizando para sincronización no use lock(this) en su implementación (Es interesante, pero era una forma recomendada de sincronización por parte de Microsoft para algún tiempo ... Entonces, descubrieron que era un error), por lo que siempre es una buena idea usar un objeto separado especial para la sincronización. Entonces, como puede ver, OptionB no le dará una garantía de estancamiento: seguridad. Por lo tanto, OptionA es mucho más seguro que OptionB.

+0

No está respondiendo realmente la pregunta, que trata sobre 2 formas de usar un objeto que no sea 'this'. –

+0

Puede ser que mi respuesta no fue lo suficientemente clara, la he editado un poco. –

0

OptionA es la manera de hacerlo aquí, siempre y cuando en todo su código, al acceder al m_hash utilice el m_Locker para fijarlo.

Ahora imagine este caso. Bloquea el objeto. Y ese objeto en una de las funciones que llama tiene un segmento de código lock(this). En este caso, es un punto muerto irrecuperable irrecuperable

Cuestiones relacionadas