2012-02-07 39 views
43

me pregunto si esta construcción se producirá un error:bloqueo en el interior de bloqueo

lock(sync) 
{ 
    // something 
    lock(sync) 
    { 
    //something 
    lock(sync) 
    { 
     //something 
    } 
    } 
} 

me he encontrado este código, y parece muy bien, pero tal vez en algunas circunstancias, un error puede ser lanzado?

+0

¿Por qué desea utilizar una construcción así? – Jehof

+0

¿Puedes describir alguna situación en la que necesites hacerlo? Estoy tratando de entender eso, ¿pero ...? –

+0

@ MatíasFidemraizer Una función recursiva puede bloquear el mismo recurso. –

Respuesta

38

lock is a wrapper for Monitor.Enter and Monitor.Exit:

The lock keyword calls Enter at the start of the block and Exit at the end of the block. From the former's documentation:

From the documentation for Monitor.Enter:

It is legal for the same thread to invoke Enter more than once without it blocking; however, an equal number of Exit calls must be invoked before other threads waiting on the object will unblock.

Debido a que se emparejan las llamadas a Enter y Exit, su patrón de código tiene bien definido el comportamiento.

Tenga en cuenta, sin embargo, que se lock no garantiza que sea una construcción de excepciones menos:

A ThreadInterruptedException is thrown if Interrupt interrupts a thread that is waiting to enter a lock statement.

+2

Merece la pena señalar que las 'cerraduras 'internas son esencialmente redundantes: usted ya * tiene * la cerradura; pedirlo de nuevo nunca fallará. –

+9

Realmente no vale la pena señalar eso, ya que es común que los métodos que tienen bloqueos invoquen métodos que también obtengan bloqueos. En algunos diseños, el segundo bloqueo se estanca ... Trabajé en tales sistemas, y era necesario que el programa mantuviera su propio recuento de referencias y solo tomara y liberara el bloqueo en el nivel más externo. –

+0

Estoy de acuerdo con Jim.Acabo de tener una instancia en la que en una aplicación MVC necesitaba un bloqueo interno. Elegí colocar una declaración de bloqueo en las inserciones y actualizaciones con la misma clave, y luego tengo un área en el controlador que bloqueo, donde cierro el informe; esto recopila datos para un informe de errores, luego llama a los métodos DAL necesarios, algunos métodos que se reutilizan y otros procesos que pueden invocar fuera del informe. Quería congelar cualquier cambio en la base de datos durante el cierre, por lo que el informe de error es exacto y no se filtra nada. Así que sí, hay razones por las que un bloqueo anidado no es redundante. – eaglei22

0

De acuerdo con MSDN (ver here y here) este es un comportamiento bien definido y no causa ningún problema.

12

para explicar el comportamiento por lo que está bien definido y nunca fallará:

Aparte: This answer has better details about how locks actually work

El bloqueo se produce a nivel Thread, así que llamar por segunda vez en el mismo subproceso será redundante . Pensaría que no tendría ninguna penalización de rendimiento (aunque eso dependería de cómo se escriban exactamente las partes internas de .Net, por lo que no puedo garantizarlo)

Muchas veces tendría una función pública que llama a otra función pública en su clase, que ambos necesitan el candado cuando se usan por separado. Si no se permitiera esto, fallaría lo siguiente:

private Dictionary<string, int> database = new Dictionary<string, int>(); 
private object databaseLock = new object(); 
public void AddOrUpdate(string item) 
{ 
    lock (databaseLock) 
    { 
     if (Exists(item)) 
      database.Add(item, 1); 
     else 
      ++database[item]; 
    } 
} 
public bool Exists(string item) 
{ 
    lock (databaseLock) 
    { 
     //... Maybe some pre-processing of the key or item... 
     return database.ContainsKey(item); 
    } 
} 
+0

gracias, estaba tratando de averiguar si el bloqueo dentro del bloqueo del mismo objeto funcionaría, precisamente porque un método llama a otro. – mcmillab