¿Un objeto bloqueado permanece bloqueado si se produce una excepción dentro de él?

En la aplicación ac # threading, 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:

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 desbloqueará la cerradura.

Primero; ¿Has considerado TryParse?

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

El locking se lanzará por 2 razones; primero, el 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 la 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 catastróficas, excepto las más fatales.

Observo que nadie ha mencionado en sus respuestas a esta vieja pregunta que liberar una cerradura de una excepción es algo increíblemente peligroso. Sí, las declaraciones de locking 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 terminar el proceso enfermo inmediatamente antes de que destruya más datos de usuario , no libere el locking 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ó allí y mató a alguien! Probablemente se haya destruido la tubería, la casa ya no esté 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.

Es decir, piénsalo bien: si bloqueaste una región de código para leer desde una estructura de datos sin que se mudara en otro hilo, y algo en esa estructura de datos arrojó una excepción, es probable que sea porque la estructura de datos es corrupto Los datos del usuario ahora están en mal estado; no desea intentar guardar los datos del 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, seguro que ahora . Cuál es exactamente el escenario que se supone que protege la cerradura. Ahora el código que está esperando para leer ese estado tendrá acceso inmediato 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 locking es una mala noticia . 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 progtwig para que las mutaciones vuelvan a los buenos estados previos?”

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

Si realmente quieres saber, un locking en C # 4 / .NET 4 es:

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

“Una statement de locking se comstack a una llamada a Monitor.Enter, y luego a un bash … finalmente bloquear. En el bloque finally, se llama a Monitor.Exit.

La generación de código JIT para x86 y x64 garantiza que no se produzca un aborto de subprocesos entre una llamada a Monitor.Enter y un bloque try que lo sigue inmediatamente “.

Tomado de: este sitio

Su locking se lanzará correctamente. Un lock actúa así:

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

Y finally se garantiza que los bloques se ejecutarán, sin importar cómo salgas del bloque try .

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

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

Si se ve obligado a utilizar Monitor.Enter / Exit por ejemplo, para admitir un tiempo de espera, debe asegurarse de realizar la llamada a Monitor.Exit en el bloque finally para garantizar la liberación adecuada del locking en caso de una excepción.