Curso de C++ Builder


Programación con Hebras

Ejecución de las hebras



El método Execute es la función principal de una hebra. Una hebra puede concebirse como un programa que se ejecuta dentro de la aplicación. Una hebra no es un programa independiente porque comparte el mismo espacio con las demás hebras de la aplicación. Por tanto, es necesario asegurarse de no escribir en la memoria que utilizan las otras hebras. Por otro lado, puede utilizarse la memoria compartida entre las hebras para establecer una comunicación entre ellas.

Ejemplo:

  • En HPelota.cpp:
    void __fastcall THebraPelota::Execute()
    {
      while (true) {     // Código de la Hebra.
    
        try {
          Pelota->Mover();
        } catch (Exception &error) {
        }
    
        Sleep(100);
      }
    }
    
  • Variables locales a las hebras

    Con la palabra clave __thread podemos definir variables globales que son locales a una hebra. Habrá una copia de la variable para cada una de las hebras que se creen: la variable es global para las funciones de una hebra pero no se comparte con otras hebras.

    Ejemplo:

    
    int __thread x;
    

    Las variables __thread han de ser estáticas (no pueden ser punteros) y no pueden inicializarse en tiempo de ejecución).

    No es aconsejable utilizar este tipo de variables, ya que se puede conseguir lo mismo añadiendo una variable de instancia a la clase de la hebra.

    Control de la ejecución de una hebra

    Una hebra puede encontrarse en dos estados:

    Las hebras pueden iniciarse y suspenderse tantas veces como sea necesario antes de que finalice su ejecución. Para detener una hebra temporalmente hay que realizar una llamada al método Suspend(). Cuando resulte conveniente reanudar la ejecución de la hebra se debe realizar una llamada al método Resume(). Suspend() y Resume() utilizan un contador interno, de forma que es posible anidar las llamadas a Suspend() y Resume(). La hebra no reanuda su ejecución hasta que todas las suspensiones hayan tenido su correspondiente llamada a Resume().

    La ejecución de una hebra termina normalmente cuando termina la ejecución del método Execute().

    Para hacer que la ejecución de una hebra finalice de forma prematura hay realizar una llamada al método Terminate(). Terminate() cambia el valor de la propiedad Terminated a true, indicando así que la hebra ha de finalizar su ejecución en cuanto sea posible. Por tanto, el método Execute() ha de comprobar periódicamente el valor de la propiedad Terminated y detener la ejecución de la hebra cuando esté a true.

    Ejemplo:

  • En HPelota.cpp:
    void __fastcall THebraPelota::Execute()
    {
      while (!Terminated) {     // Código de la Hebra.
    
        try {
          Pelota->Mover();
        } catch (Exception &error) {
        }
    
        Sleep(100);
      }
    }
    
  • Cuando se crea una hebra, el estado inicial de ésta dependerá del parámetro que se le pase a su constructor: ejecutándose (false) o suspendida (true). Si se crea una hebra inicialmente suspendida se ha de invocar al método Resume() para comenzar su ejecución.

    Ejemplo:

  • En Ppal.h:
    Para usar la unidad HPelota desde un formulario:
    #include "HPelota.h"
    ...
    private:	// User declarations
      THebraPelota **Objs;
    ...
    
  • En Ppal.cpp:
    #include <stdlib.h>
    
    ...
    
    void __fastcall TPpalFrm::FormCreate(TObject *Sender)
    {
      Objs = new THebraPelota*[4];
    
      randomize();
    
      for (int i=0; i<4; ++i)
          Objs[i] = new THebraPelota (PaintBox);
    }
    
    void __fastcall TPpalFrm::FormDestroy(TObject *Sender)
    {
      for (int i=0; i<4; ++i)
          Objs[i]->Terminate();
    
      Sleep(3000); // Esperar tres segundos...
                   // ...a que se liberen todas las hebras
      delete[] Objs;
    }
    

    Nota: La sentencia Sleep(3000), es una chapuza para evitar que se “cuelgue” la aplicación al liberar las hebras (más adelante veremos como solucionar este problema correctamente).



  • Índice de la sección