¿Cómo funciona exactamente el bloqueo?

527

Veo que para usar objetos que no son seguros para subprocesos envolvemos el código con un bloqueo como este:

private static readonly Object obj = new Object();

lock (obj)
{
    // thread unsafe code
}

Entonces, ¿qué sucede cuando varios subprocesos acceden al mismo código (supongamos que se ejecuta en una aplicación web ASP.NET). ¿Están en cola? Si es así, ¿cuánto tiempo esperarán?

¿Cuál es el impacto en el rendimiento debido al uso de bloqueos?

NLV
fuente
1
^ enlace muerto, ver: jonskeet.uk/csharp/threads/index.html
Ivan Pavičić

Respuestas:

448

La lockdeclaración es traducida por C # 3.0 a la siguiente:

var temp = obj;

Monitor.Enter(temp);

try
{
    // body
}
finally
{
    Monitor.Exit(temp);
}

En C # 4.0 esto ha cambiado y ahora se genera de la siguiente manera:

bool lockWasTaken = false;
var temp = obj;
try
{
    Monitor.Enter(temp, ref lockWasTaken);
    // body
}
finally
{
    if (lockWasTaken)
    {
        Monitor.Exit(temp); 
    }
}

Puedes encontrar más información sobre lo que Monitor.Enterhace aquí . Para citar MSDN:

Use Enterpara adquirir el Monitor en el objeto pasado como parámetro. Si otro hilo ha ejecutado un Enter en el objeto pero aún no ha ejecutado el correspondiente Exit, el hilo actual se bloqueará hasta que el otro hilo libere el objeto. Es legal que el mismo hilo invoque Entermás de una vez sin bloquearlo; sin embargo, se Exitdebe invocar un número igual de llamadas antes de que otros hilos que esperan en el objeto se desbloqueen.

El Monitor.Entermétodo esperará infinitamente; No se agotará el tiempo.

Steven
fuente
15
Según MSDN "Generalmente se prefiere usar la palabra clave lock (C #) o SyncLock (Visual Basic) en lugar de usar la clase Monitor directamente, tanto porque lock o SyncLock es más conciso como porque lock o SyncLock aseguran que el monitor subyacente se libere, incluso si el código protegido arroja una excepción. Esto se logra con la palabra clave finalmente, que ejecuta su bloque de código asociado independientemente de si se produce una excepción ". msdn.microsoft.com/en-us/library/ms173179.aspx
Aiden Strydom
10
¿Cuál es el punto de var temp = obj; línea. Como para empezar es solo una referencia, ¿de qué sirve hacer a otro?
priehl
11
@priehl Permite al usuario cambiar objsin que todo el sistema quede en punto muerto.
Steven
77
@Joymon eventualmente, cada característica del lenguaje es azúcar sintáctica. Las características del lenguaje tratan de hacer que los desarrolladores sean más productivos y hacer que las aplicaciones sean más fáciles de mantener, como también lo es la función de bloqueo.
Steven
2
Correcto. Este es todo el propósito de la lockdeclaración y el monitor: para que pueda realizar una operación en un hilo sin tener que preocuparse de que otro hilo lo arruine.
Dizzy H. Muffin
285

Es más simple de lo que piensas.

Según Microsoft : la lockpalabra clave asegura que un hilo no entre en una sección crítica de código mientras otro hilo está en la sección crítica. Si otro hilo intenta ingresar un código bloqueado, esperará, bloqueará, hasta que se libere el objeto.

La lockpalabra clave llama Enteral comienzo del bloque y Exital final del bloque. lockLa palabra clave realmente maneja la Monitorclase en el back-end.

Por ejemplo:

private static readonly Object obj = new Object();

lock (obj)
{
    // critical section
}

En el código anterior, primero el hilo entra en una sección crítica, y luego se bloqueará obj. Cuando otro subproceso intenta ingresar, también intentará bloquear obj, que ya está bloqueado por el primer subproceso. El segundo hilo tendrá que esperar a que se libere el primer hilo obj. Cuando sale el primer hilo, otro se bloqueará objy entrará en la sección crítica.

Umar Abbas
fuente
99
¿deberíamos crear un objeto ficticio para bloquear o podemos bloquear una variable existente en el contexto?
batmaci
99
@batmaci: el bloqueo en un objeto ficticio privado separado le garantiza que nadie más lo bloqueará. Si bloquea los datos y ese mismo dato es visible desde el exterior, pierde esa garantía.
Umar Abbas
8
¿Qué sucede si hay más de un proceso esperando que se libere el bloqueo? ¿Están en cola los procesos de espera para que bloqueen la sección crítica en orden FIFO?
jstuardo
@jstuardo: están en cola, pero no se garantiza que el pedido sea FIFO. Echa un vistazo a este enlace: albahari.com/threading/part2.aspx
Umar Abbas el
Copiado sin atribución de net-informations.com/faq/qk/lock.htm
Martijn Pieters
47

No, no están en cola, están durmiendo.

Una declaración de bloqueo del formulario

lock (x) ... 

donde x es una expresión de un tipo de referencia, es precisamente equivalente a

var temp = x;
System.Threading.Monitor.Enter(temp); 
try { ... } 
finally { System.Threading.Monitor.Exit(temp); }

Solo necesita saber que se están esperando el uno al otro, y solo ingresará un hilo para bloquear el bloqueo, los otros esperarán ...

Monitor está escrito completamente en .net, por lo que es lo suficientemente rápido, también mire la clase Monitor con reflector para obtener más detalles

Arsen Mkrtchyan
fuente
66
Tenga en cuenta que el código emitido para la lockdeclaración cambió ligeramente en C # 4: blogs.msdn.com/b/ericlippert/archive/2009/03/06/…
LukeH
@ArsenMkrt, no se mantienen en estado "bloqueado" ¿Cola? Creo que hay alguna diferencia entre el estado de suspensión y bloqueo, ¿no es así?
Mohanavel
¿A qué diferencia te refieres @Mohanavel?
Arsen Mkrtchyan
1
Esa no era la pregunta. La pregunta era sobre la palabra clave "bloquear". Supongamos que un proceso ingresa a una sección de "bloqueo". Eso significa que el proceso bloquea ese fragmento de código y que ningún otro proceso podrá ingresar a esa sección hasta que se libere el bloqueo. Bueno ... ahora, 2 procesos más intentan ingresar al mismo bloque. Como está protegido por la palabra clave "bloqueo", esperarán, de acuerdo con lo que se dijo en este foro. Cuando el primer proceso libera el bloqueo. ¿Qué proceso ingresa al bloque? ¿el primero que intentó ingresar o el último?
jstuardo
1
Supongo que te refieres a hilo en lugar de proceso ... si es así, la respuesta es No, no hay garantía de cuál ingresará ... más aquí stackoverflow.com/questions/4228864/…
Arsen Mkrtchyan
29

Los bloqueos bloquearán que otros hilos ejecuten el código contenido en el bloque de bloqueo. Los hilos tendrán que esperar hasta que el hilo dentro del bloque de bloqueo se haya completado y se libere el bloqueo. Esto tiene un impacto negativo en el rendimiento en un entorno multiproceso. Si necesita hacer esto, debe asegurarse de que el código dentro del bloque de bloqueo pueda procesarse muy rápidamente. Debe intentar evitar actividades costosas como acceder a una base de datos, etc.

Andrés
fuente
11

El impacto en el rendimiento depende de la forma en que se bloquea. Puede encontrar una buena lista de optimizaciones aquí: http://www.thinkingparallel.com/2007/07/31/10-ways-to-reduce-lock-contention-in-threaded-programs/

Básicamente, debe tratar de bloquear lo menos posible, ya que pone su código de espera para dormir. Si tiene algunos cálculos pesados ​​o un código de larga duración (por ejemplo, carga de archivos) en un bloqueo, se produce una gran pérdida de rendimiento.

Simon Woker
fuente
1
Pero tratar de escribir código de bloqueo bajo a menudo puede provocar errores sutiles, difíciles de encontrar y corregir, incluso si es un experto en el campo. Usar un candado es a menudo el menor de dos males. ¡Debe bloquear exactamente tanto como lo necesite, ni más ni menos!
LukeH
1
@LukeH: Hay algunos patrones de uso en los que el código de bloqueo bajo puede ser muy simple y fácil [ do { oldValue = thing; newValue = updated(oldValue); } while (CompareExchange(ref thing, newValue, oldValue) != oldValue]. El mayor peligro es que si los requisitos evolucionan más allá de lo que pueden manejarse tales técnicas, puede ser difícil adaptar el código para manejar eso.
supercat
El enlace se ha roto.
CarenRose
8

La parte dentro de la declaración de bloqueo solo puede ejecutarse mediante un subproceso, por lo que todos los demás subprocesos esperarán indefinidamente a que finalice el subproceso que mantiene el bloqueo. Esto puede resultar en un llamado punto muerto.

Mr47
fuente
8

La lockdeclaración se traduce en llamadas a los métodos Entery Exitde Monitor.

La lockdeclaración esperará indefinidamente a que se libere el objeto de bloqueo.

Paolo Tedesco
fuente