example c# multithreading thread-safety using-statement abort

example - ¿C#es usando la declaración abort-safe?



c# summary example (9)

¿Son correctos los autores del libro y la declaración de uso no es abort-safe o están equivocados y se comporta como en mi segunda solución?

De acuerdo con el libro (pág. 856), la excepción ThreadAbortException puede lanzarse en cualquier lugar en el código administrado. ¿Pero tal vez hay excepciones y la primera variante es abort-safe después de todo?

Los autores tienen razón. El bloque de using no es abort-seguro. Su segunda solución tampoco es abortiva, el hilo podría abortarse en medio de la adquisición de recursos.

Aunque no es abort-safe, cualquier desechable que tenga recursos sin cambios también debe implementar un finalizador, que eventualmente ejecutará y limpiará el recurso. El finalizador debe ser lo suficientemente robusto como para no ocuparse de objetos completamente inicializados, en caso de que el hilo se interrumpa en medio de la adquisición de recursos.

Un Thread.Abort solo esperará el código que se ejecuta dentro de las Regiones de ejecución restringida (CER), finally bloques, catch bloques de catch , los constructores estáticos y el código no administrado. Así que esta es una solución segura para abortar ( solo con respecto a la adquisición y eliminación del recurso):

StreamReader reader = null; try { try { } finally { reader = File.OpenText("file.txt"); } // ... } finally { if (reader != null) reader.Dispose(); }

Pero tenga cuidado , el código abort-safe debería ejecutarse rápido y no bloquearse . Podría colgar una aplicación completa de la operación de descarga de dominio.

Si el uso es equivalente a la primera variante (no es seguro para abortar), ¿por qué finalmente se comprueba si hay un valor nulo?

La comprobación de nulo hace que el using patrones sea seguro en presencia de referencias null .

Acabo de terminar de leer "C # 4.0 en una cáscara de nuez" (O''Reilly) y creo que es un gran libro para un programador que desea cambiar a C #, pero me dejó pensando. Mi problema es la definición de using declaración. Según el libro (pág. 138),

using (StreamReader reader = File.OpenText("file.txt")) { ... }

es precisamente equivalente a:

StreamReader reader = File.OpenText("file.txt"); try { ... } finally { if (reader != null) ((IDisposable)reader).Dispose(); }

Supongamos, sin embargo, que esto es cierto y que este código se ejecuta en un hilo separado. Este hilo ahora se aborta con thread.Abort() , por lo que se thread.Abort() una ThreadAbortException y supone que el hilo se encuentra exactamente después de inicializar el lector y antes de ingresar a la cláusula try..finally . Esto significaría que el lector no está dispuesto!

Una posible solución sería codificar de esta manera:

StreamReader reader = null; try { reader = File.OpenText("file.txt"); ... } finally { if (reader != null) ((IDisposable)reader).Dispose(); }

Esto sería abort-seguro.

Ahora para mis preguntas:

  1. ¿Son correctos los autores del libro y la declaración de using no es abort-safe o están equivocados y se comporta como en mi segunda solución?
  2. Si el using es equivalente a la primera variante (no es seguro para abortar), ¿por qué finally se comprueba si hay un null ?
  3. De acuerdo con el libro (pág. 856), la ThreadAbortException puede lanzarse en cualquier lugar en el código administrado. ¿Pero tal vez hay excepciones y la primera variante es abort-safe después de todo?

EDIT: Sé que usar thread.Abort() no se considera una buena práctica. Mi interés es puramente teórico: ¿cómo se comporta exactamente la declaración de using ?


El primero es, de hecho, exactamente equivalente al segundo.

Como ya se señaló, ThreadAbort es algo malo, pero no es lo mismo que matar la tarea con el Administrador de tareas o apagar la PC.

ThreadAbort es una excepción administrada, que el tiempo de ejecución aumentará cuando sea posible, y solo entonces.

Dicho esto, una vez que te gusta ThreadAbort, ¿por qué molestarse en intentar limpiar? Estás en la agonía de la muerte de todos modos.


El sitio web complementario del libro tiene más información sobre las hebras de aborto here .

En resumen, la primera traducción es correcta (se puede ver mirando el IL).

La respuesta a su segunda pregunta es que puede haber escenarios donde la variable puede ser legítimamente nula. Por ejemplo, GetFoo () puede devolver un valor nulo aquí, en el que no querría que se lance una NullReferenceException en el bloque implícito finalmente:

using (var x = GetFoo()) { ... }

Para responder a su tercera pregunta, la única manera de hacer que Abort sea seguro (si está llamando al código de Framework) es eliminar el dominio de aplicación después. Esta es en realidad una solución práctica en muchos casos (es exactamente lo que hace LINQPad cada vez que cancela una consulta en ejecución).


La especificación del lenguaje establece claramente que la primera es correcta.

http://msdn.microsoft.com/en-us/vcsharp/aa336809.aspx MS Spec (documento de Word)
http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-334.pdf ECMA Spec

En caso de que un hilo aborte, ambas variantes de código pueden fallar. El segundo, si la anulación se produce después de que la expresión se haya evaluado pero antes de que se produzca la asignación a la variable local.

Pero no deberías usar el aborto de hilos de todos modos ya que puede corromper fácilmente el estado del dominio de la aplicación. Por lo tanto, solo abortar subprocesos si fuerza la descarga de un dominio de aplicación


La sentencia finally siempre se ejecuta, MSDN dice que "finalmente se usa para garantizar que un bloque de instrucción de código se ejecute independientemente de cómo se salga del bloque try anterior".

Por lo tanto, no tiene que preocuparse por no limpiar los recursos, etc. (solo si Windows, el Framework-Runtime o cualquier otra cosa mala que no pueda controlar sucede, pero luego hay problemas más grandes que la limpieza de los Recursos ;-))


Realmente no hay diferencia entre sus dos escenarios: en el segundo, ThreadAbort aún podría ocurrir después de la llamada a OpenText, pero antes de que el resultado se asigne al lector.

Básicamente, todas las apuestas están desactivadas cuando se obtiene una ThreadAbortException. Es por eso que nunca debe abortar los hilos intencionalmente en lugar de usar algún otro método para cerrar el hilo con gracia.

En respuesta a su edición, quisiera señalar nuevamente que sus dos escenarios son en realidad idénticos. La variable ''lector'' será nula a menos que la llamada a File.OpenText se complete con éxito y devuelva un valor, por lo que no hay diferencia entre escribir el código de la primera forma en lugar de la segunda.


Te estás enfocando en el problema equivocado. La excepción ThreadAbortException tiene la misma probabilidad de abortar el método OpenText (). Puedes esperar que sea resistente a eso, pero no lo es. Los métodos de marco no tienen cláusulas try / catch que intenten lidiar con un aborto de subproceso.

Tenga en cuenta que el archivo no permanece abierto para siempre. Eventualmente, el finalizador de FileStream cerrará el identificador de archivo. Por supuesto, esto todavía puede causar excepciones en su programa cuando continúa ejecutándose e intenta abrir el archivo nuevamente antes de que se ejecute el finalizador. Aunque esto es algo con lo que siempre tiene que estar a la defensiva cuando ejecuta un sistema operativo multitarea.


Un poco fuera de lugar, pero el comportamiento de la declaración de bloqueo durante el aborto de subprocesos también es interesante. Mientras que el bloqueo es equivalente a:

object obj = x; System.Threading.Monitor.Enter(obj); try { … } finally { System.Threading.Monitor.Exit(obj); }

Está garantizado (por el J86ter x86) que la anulación del hilo no se produce entre Monitor.Enter y la declaración de prueba.
http://blogs.msdn.com/b/ericlippert/archive/2007/08/17/subtleties-of-c-il-codegen.aspx

El código IL generado parece ser diferente en .net 4:
http://blogs.msdn.com/b/ericlippert/archive/2009/03/06/locks-and-exceptions-do-not-mix.aspx


Thread.Abort es muy muy mal juju; Si la gente está llamando, ya estás en un montón de problemas (bloqueos irrecuperables, etc.). Thread.Abort debe Thread.Abort realmente al scanerio de inhalar un proceso enfermizo.

Las excepciones generalmente se desenrollan limpiamente, pero en casos extremos no hay garantía de que cada bit de código pueda ejecutarse. Un ejemplo más urgente es "¿Qué sucede si falla la alimentación?".

Re el cheque null ; ¿Qué File.OpenText si File.OpenText devuelve null ? OK, no lo hará, pero el compilador no lo sabe.