El misterio del bloque try vacio

Table of Contents [hide]

El misterio

Hace bien poco he leido un interesante artículo (en inglés) llamado "The empty try block mystery" que describe algunas secciones de código presentes en el código fuente del .NET Framework y que son similares a lo siguiente:

try
{
}
finally
{
  HazCosas1();
  HazCosas2();
}

y que responde a la pregunta ¿porque diablos iba alguien a declarar un bloque try finally estando el try vacío? Pues vamos a explicarlo, en español ;)

El por qué

El uso del bloque de código anterior es una forma de prevenir o protegerse frente a una posible invocación de una llamada a Thread.Abort que interrumpe la ejecución de un hilo.

El modo en el que dicha ejecución se interrumpe consiste en que, cuando se invoca la llamada, en dicho hilo se levanta un Thread Abort Exception se encuentre la ejecución de la tarea en el punto que se encuentre. Ahora bien, el código, más o menos estándar, que podemos esperar en el método principal de un hilo (el que corresponde a un delegado de tipo Thread Start) suele ser del siguiente estilo:

public void DoWork()
{
  try
  {
    // El hilo está activo hasta que se cumple una determinada condicion
    while (condicion)
    {
      // Ejecutar el trabajo (por ejemplo consumir en un productor/consumidor)
      Trabajo1;
      Trabajo2;
      Trabajo3;
    }
  }
  catch (ThreadAbortException e)
  {
     // Alguien abortó la tarea realizamos lo que sea necesario
  }
  finally
  {
     // Limpiar recursos, etc
  }
}

Ahora bien, vamos a plantear una situación en la que estamos haciendo algo que no queremos que sea interrumpido, por cualquier razón; una serie de comandos que deben realizarse antes de que el hilo termine puesto que de lo contrario la aplicación podría quedar en un estado inconsistente. En el ejemplo anterior supongamos que es imprescindible que se realicen los trabajos 2 y 3 en cualquier caso. Si llega un ThreadAbortException mientras se está ejecutando el trabajo 2, dicho trabajo se verá interrumpido (porque además estas excepciones no son enmascarables lo cual implica que cuando el control del programa llega al final del bloque ''catch'' la excepción es relanzada).

Una posible solución sería detectar, en el catch, en que punto nos hemos quedado (teniendo en cuenta que podríamos habernos quedado en mitad de la ejecución del trabajo2) y proseguir la ejecución desde ese punto (claro que si se recibiera otro Thread.Abort estaríamos en la misma situación).

Protegernos de esta situación es lo que da lugar a que se produzca el segmento de código con un try vacio. El bloque finally no es interrumpible por un Thread.Abort por lo que su ejecución está garantizada. Si necesitamos que los trabajos 1, 2 y 3 se ejecuten (todos o ninguno) completamente antes de abortar la tarea acabaremos con un código como el siguiente:

public void DoWork()
{
  try
  {
    // El hilo está activo hasta que se cumple una determinada condicion
    while (condicion)
    {
      try
      {
      }
      finally
      {
        // Ejecutar el trabajo (por ejemplo consumir en un productor/consumidor)
        Trabajo1;
        Trabajo2;
        Trabajo3;
      }
    }
  }
  catch (ThreadAbortException e)
  {
     // Alguien abortó la tarea realizamos lo que sea necesario
  }
  finally
  {
     // Limpiar recursos, etc
  }
}

y de esta forma, cuando se produzca un Thread.Abort, puesto que dicha llamada no puede interrumpir un bloque finally estamos garantizando que los tres trabajos se ejecuten y solo después se aborte la tarea.

6
Average: 6 (2 votes)
Your rating: None