2009-02-26 13 views
67

En una aplicación de subprocesamiento C#, si tuviera que bloquear un objeto, digamos una cola, y si ocurre una excepción, ¿el objeto permanecerá bloqueado? Aquí está el pseudo-código:¿Permanece bloqueado un objeto bloqueado si se produce una excepción dentro de él?

int ii; 
lock(MyQueue) 
{ 
    MyClass LclClass = (MyClass)MyQueue.Dequeue(); 
    try 
    { 
     ii = int.parse(LclClass.SomeString); 
    } 
    catch 
    { 
    MessageBox.Show("Error parsing string"); 
    } 
} 

Según tengo entendido, el código después de la captura no se ejecuta - pero me he estado preguntando si se liberará el bloqueo.

+1

Como reflexión final (ver actualizaciones) - probablemente solo deberías mantener el bloqueo mientras dure la secuencia ... haz el procesamiento ** fuera ** de la cerradura. –

+1

Código después de la captura se ejecuta porque se maneja la excepción – cjk

+0

Gracias, debo haber perdido esa, ¿debería eliminar esta pregunta? – Vort3x

Respuesta

70

Primero; ¿Has considerado TryParse?

in li; 
if(int.TryParse(LclClass.SomeString, out li)) { 
    // li is now assigned 
} else { 
    // input string is dodgy 
} 

El bloqueo se lanzará por 2 razones; primero, lock es esencialmente:

Monitor.Enter(lockObj); 
try { 
    // ... 
} finally { 
    Monitor.Exit(lockObj); 
} 

Segundo; captas y no vuelves a lanzar la excepción interna, por lo que el lock nunca ve una excepción. Por supuesto, está presionando el candado mientras dure un MessageBox, lo que podría ser un problema.

Por lo tanto, se lanzará en todas las excepciones irrecuperables pero catastróficas más mortales.

+11

Soy consciente de la tryparse, pero no es realmente relevante para mi pregunta. Este fue un código simple para explicar la pregunta, no una verdadera preocupación con respecto al análisis. Reemplace el análisis por * cualquier * código que forzará la captura y lo hará sentir cómodo. – Khadaji

+10

¿Qué tal lanzar nueva excepción ("con fines ilustrativos"); ;-p –

+1

Excepto si ocurre una 'TheadAbortException' entre' Monitor.Enter' y 'try': http: //blogs.msdn.com/ericlippert/archive/2009/03/06/locks-and-exceptions-do-not-mix.aspx –

4

Solo para agregar un poco a la excelente respuesta de Marc.

Situaciones como esta son la verdadera razón de la existencia de la palabra clave lock. Ayuda a los desarrolladores a asegurarse de que se libere el bloqueo en el bloque finally.

Si está obligado a usar Monitor.Enter/Exit p. para admitir un tiempo de espera, debe asegurarse de realizar la llamada al Monitor.Exit en el bloque finally para garantizar la liberación adecuada del bloqueo en caso de una excepción.

13

"Una declaración de bloqueo se compila a una llamada a Monitor.Enter, y luego una oportunidad ... finally. En el bloque finally, Monitor.Exit se llama.

La generación de código JIT tanto para x86 y x64 asegura que un aborto de subprocesos no puede ocurrir entre una llamada de Monitor.Enter y un bloque de prueba que lo sigue inmediatamente ".

Tomado de: This site

+0

Hay al menos un caso en el que esto no es cierto: el aborto de subprocesos en modo de depuración en versiones de .net antes que 4. La razón es que el compilador de C# inserta un NOP entre 'Monitor.Enter' y' try', por lo que que se viola la condición de "seguir inmediatamente" del JIT. – CodesInChaos

36

sí, que dará a conocer adecuadamente; lock actúa como try/finally, con el Monitor.Exit(myLock) finalmente, por lo que no importa cómo salga será liberado. Como nota al margen, lo mejor es evitar catch(... e) {throw e;}, ya que eso daña el stack-trace en e; es mejor no atraparlo en absoluto, o alternativamente: usar throw; en lugar de throw e; que hace un nuevo lanzamiento.

Si realmente quiere saber, un bloqueo en C# 4/4 .NET es:

{ 
    bool haveLock = false; 
    try { 
     Monitor.Enter(myLock, ref haveLock); 
    } finally { 
     if(haveLock) Monitor.Exit(myLock); 
    } 
} 
5

Su bloqueo se libera correctamente. A lock actos como este:

try { 
    Monitor.Enter(myLock); 
    // ... 
} finally { 
    Monitor.Exit(myLock); 
} 

Y finally bloques están garantizados para ejecutar, no importa lo que salga del bloque try.

+0

En realidad, el código ** no ** está "garantizado" para ejecutarse (podría tirar del cable de alimentación, por ejemplo), y * no es exactamente * como se ve un cerrojo en 4.0 - [vea aquí] (http://blogs.msdn.com/b/ericlippert/archive/2009/03/06/locks-and-exceptions-do-not-mix.aspx) –

+1

@MarcGravell: Pensé en poner dos notas a pie de página sobre esos dos puntos exactos. Y luego pensé que no importaría demasiado :) – Ryan

+1

@MarcGravel: Creo que todos asumen que siempre uno no está hablando de una situación de 'desconectar', ya que no es algo sobre lo que un programador tenga control :) – Vort3x

83

Observo que nadie ha mencionado en sus respuestas a esta pregunta anterior que liberando un bloqueo ante una excepción es algo increíblemente peligroso de hacer. Sí, las sentencias de bloqueo en C# tienen semántica "finalmente"; cuando el control sale de la cerradura normal o anormalmente, se libera la cerradura. Todos hablan de esto como si fuera algo bueno, ¡pero es algo malo! Lo correcto si tiene una región bloqueada que arroja una excepción no controlada es finalizar el proceso enfermo inmediatamente antes de que destruya más datos de usuario, no libere el bloqueo y continúe.

Mírelo de esta manera: supongamos que tiene un baño con un candado en la puerta y una fila de personas esperando afuera. Una bomba en el baño se apaga, matando a la persona que está allí. Su pregunta es "¿en esa situación se desbloqueará automáticamente la cerradura para que la siguiente persona pueda entrar al baño?" Sí, lo hará. Eso no es algo bueno. ¡Una bomba estalló y mató a alguien! La tubería probablemente se destruyó, la casa ya no es estructuralmente sólida y podría haber otra bomba allí. Lo correcto es sacar a todos lo más rápido posible y demoler toda la casa.

Quiero decir, creo que a través: si usted se trabó una región de código con el fin de leer de una estructura de datos sin que se mutó en otro hilo, y algo en esa estructura de datos inició una excepción, más probable es que es porque la estructura de datos está corrupta. Los datos del usuario ahora están en mal estado; no desea intente guardar datos de usuario en este punto porque está guardando datos corruptos. Solo termine el proceso.

Si bloqueó una región de código para realizar una mutación sin otro hilo leyendo el estado al mismo tiempo, y la mutación arroja, entonces si los datos no estaban corruptos antes, ahora es. ¿Cuál es exactamente el escenario que se supone que el bloqueo protege contra. Ahora el código que está esperando para leer ese estado se inmediatamente se le dará acceso al estado dañado, y probablemente se bloquee. Nuevamente, lo correcto es terminar el proceso.

No importa cómo lo cortes, una excepción dentro de un bloqueo es malas noticias. La pregunta correcta es "¿se limpiará mi cerradura en caso de una excepción?" La pregunta correcta es "¿cómo me aseguro de que nunca haya una excepción dentro de un candado? Y si lo hay, ¿cómo estructuraré mi programa para que las mutaciones vuelvan a los buenos estados previos?"

+12

Ese problema es bastante ortogonal al bloqueo de OMI. Si obtiene una excepción esperada, quiere limpiar todo, incluidos los bloqueos. Y si obtiene una excepción inesperada, tiene un problema, con o sin bloqueos. – CodesInChaos

+8

Creo que la situación descrita arriba es un generalismo. Algunas veces las excepciones describen eventos catastróficos. A veces no lo hacen. Cada uno los usa de manera diferente en el código. Es perfectamente válido que una excepción sea una señal de un evento excepcional pero no catastrófico; suponiendo excepciones = catastróficas, el caso de terminación del proceso es demasiado específico. El hecho de que podría ser un evento catastrófico no elimina la validez de la pregunta: la misma línea de pensamiento podría llevarlo a no manejar ninguna excepción, en cuyo caso se abandonará el proceso ... –

+1

@GerasimosR: De hecho. Dos puntos para enfatizar. En primer lugar, se debe asumir que las excepciones son catastróficas hasta que se determine que son benignas. En segundo lugar, si obtiene una excepción benigna echada fuera de una región bloqueada, entonces la región bloqueada probablemente esté mal diseñada; probablemente está haciendo demasiado trabajo dentro de la cerradura. –

Cuestiones relacionadas